©2008 - 2014 The original authors.

本文档的副本可以供自己使用和分发给别人,只要你不收取任何费用的副本和进一步提供,每个副本包含版权声明,是否分布在打印或电子。
表的内容

前言

1。 项目元数据

2。 使用Spring Data Repositories

Spring的目标数据存储库的抽象是显著减少所需的样板代码来实现各种持久性存储的数据访问层。

Spring数据存储库文件和你的module层

本章解释了Spring的核心概念和接口数据存储库。 在本章的信息从Spring数据共享模块。 它使用的配置和代码示例Java Persistence API(JPA)模块。 适应XML名称空间声明和类型扩展到您正在使用的特定模块的等价物。 名称空间引用 涵盖了所有支持XML配置弹簧数据模块支持存储库API, 库查询关键字 涵盖了查询关键词库支持的抽象方法。 的详细信息在您的模块的具体功能,参考本文档模块的一章。

2.1。 核心概念

在Spring Data 接口 Repository 需要domain 以及id类型域类的类型参数。 这个接口行为主要是一个标记接口获取类型一起工作,并帮助您发现接口,扩展这一个 CrudRepository 提供复杂的CRUD功能的实体类进行管理。

例1。 CrudRepository接口
public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {

    <S extends T> S save(S entity); (1)

    T findOne(ID primaryKey);       (2)

    Iterable<T> findAll();          (3)

    Long count();                   (4)

    void delete(T entity);          (5)

    boolean exists(ID primaryKey);  (6)

    // … more functionality omitted.
}
1 保存指定的泛型的实体。
2 返回的实体被给定的id。
3 返回所有实体。
4 返回的实体的数量。
5 删除给定的实体。
6 表示一个实体是否与给定id的存在。
我们还提供持久性特定于技术的抽象如。 JpaRepository MongoRepository 。 这些接口扩展 CrudRepository 持久性技术和公开的功能

CrudRepository 有一个 PagingAndSortingRepository 抽象,增加了额外的方法来缓解分页的访问实体:

例2。 PagingAndSortingRepository
public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

访问的第二页 用户 20的页面大小你可以是这样的:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

除了查询方法,计算和删除。

例3。 统计查询
public interface UserRepository extends CrudRepository<User, Long> {

  Long countByLastname(String lastname);
}
例4。 条件删除
public interface UserRepository extends CrudRepository<User, Long> {

  Long deleteByLastname(String lastname);

  List<User> removeByLastname(String lastname);

}

2.2。 查询方法

标准的CRUD功能存储库通常对底层数据存储查询。 Spring Data把这些查询变成了四个步骤的过程:

  1. 声明一个接口扩展库和类型它或者它的一个个子域类和ID类型,它将处理。

    interface PersonRepository extends Repository<User, Long> {  }
  2. 接口上新建条件查询的方法。

    interface PersonRepository extends Repository<User, Long> {
      List<Person> findByLastname(String lastname);
    }
  3. 为这些接口创建代理实例。 可以通过 JavaConfig :

    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    
    @EnableJpaRepositories
    class Config {}

    或通过 XML配置 :

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/data/jpa
         http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    
       <jpa:repositories base-package="com.acme.repositories"/>
    
    </beans>

    在本例中使用的JPA名称空间。如果您正在使用repository中的抽象为任何其他数据源,你需要改变这种适当的名称空间声明你的存储模块来与jpa支持,例如,mongodb。

  4. 获得repository 实例注入并使用它。

    public class SomeClient {
    
      @Autowired
      private PersonRepository repository;
    
      public void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
      }
    }

接下来的小节详细解释每一个步骤。

2.3。 定义repository的接口

首先定义一个domain继承Repository和输入domain和ID类型。 如果你想让CRUD方法对于该域类型,extend CrudRepository

2.3.1。 自定义接口

通常,您的存储库接口将会扩展 Repository , CrudRepository PagingAndSortingRepository 。 另外,如果你不想继承Spring Data接口,还可以注释库接口 @RepositoryDefinition 。 扩展 CrudRepository 公开了一套完整的方法来操作您的实体。 如果你喜欢选择调用方法,简单地复制你想要的曝光 CrudRepository 到你的repository。

这允许您定义自己的抽象上的弹性提供数据存储库的功能。
例5。 有选择地公开CRUD方法
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> {

  T findOne(ID id);

  T save(T entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

在这第一步你定义了一个公共基础的接口MyBaseRepository 提供 findOne() 以及 save()

注意,接口注释 @NoRepositoryBean 。 确保你添加注释

2.4。 定义查询方法

repository 代理有两种方法可以得到一个能找到方法名称查询。 它可以直接获得方法名称的查询,或者通过使用一个手动定义查询。 可用的选项取决于实际的商店。 然而,还有€™年代必须创建一个策略,决定什么实际查询。 欢喜€™看看可用的选项。

2.4.1。 查询查找策略

以下策略可供查询库基础设施来解决。 您可以配置策略名称空间通过 query-lookup-strategy 属性的XML配置或通过 queryLookupStrategy 启用的属性$ {store}库注释的Java配置。 一些策略可能不支持特定的数据存储。

  • 创建 试图构建一个能找到查询的查询方法名称。 通常的做法是把给定的一组注明前缀的方法名和解析的方法。 阅读更多关于查询建设 创建查询

  • USE_DECLARED_QUERY 试图找到一个声明查询并将抛出一个异常情况。 查询可以定义注释上。

  • CREATE_IF_NOT_FOUND (默认)结合 CREATE USE_DECLARED_QUERY 。 看起来一个声明查询第一,如果没有声明查询发现,它创建一个定制的基于名称的查询方法。 这是默认查找策略,因此如果你不使用任何显式配置。 它允许快速查询定义的方法名,还custom-tuning这些查询通过引入需要查询。

2.4.2。 创建查询

query builder机制内置为构建约束查询库的实体。 带前缀的机制 findXXBy , readAXXBy , queryXXBy , countXXBy , getaXXBy 自动解析的其余部分。 进一步引入子句可以包含表达式等 Distinct 设置不同的条件创建查询。 然而,第一个 By 作为分隔符来表示实际的标准的开始。 在一个非常基础的查询,可以定义条件 And 或者 Or

例6。 从方法名称查询创建
public interface PersonRepository extends Repository<User, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

实际结果的解析方法取决于你的持久性存储创建查询。 然而,也有一些一般要注意的事情。

  • 遍历表达式通常结合运算符连接。您可以把表达式 And Or ,Between , LessThan(不超过) , GreaterThan , Like 等运算符

  • 您可以应用静态排序通过附加一个 为基准进行排序 子句的查询方法,引用属性和方向提供了一个排序( asc Desc )。 创建一个支持动态排序的查询方法,明白了 特殊参数处理

2.4.3。 属性表达式

属性表达式只能引用的直接财产管理的实体,如前面的示例所示。 在创建查询时你已经确保解析房地产管理的域类的一个属性。 然而,您还可以定义约束通过遍历嵌套属性。 假设一个 Person 有一个 Address 与一个 Zipcode 。 在这种情况下一个方法的名称

List<Person> findByAddressZipCode(ZipCode zipCode);

创建属性遍历 x.address.zipCode 。方法执行首先解释整个部分( AddressZipCode )作为财产和检查的域类属性的名称(小写形式)。 分割源在驼峰式大小写部分从右侧头部和尾巴,试图找到对应的属性,在我们的例子中,分割为 AddressZip Code 。 分裂不匹配,该算法分割点移动到左( Address , Zipcode

您可以使用来解决这种模糊性 _ 在方法名来手动定义遍历点。 所以我们的方法名称最终将像这样:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

如果你的属性名称包含下划线(如。 first_name 中下划线),你可以逃脱的方法名与第二个下划线。 对于一个 first_name 属性查询方法必须命名 findByFirst__name(¢‚¬¦)

2.4.4。 特殊参数处理

处理参数查询只需方法参数定义为已经在上面的例子中。 除了基础查询将会认识到某些特定类型 Pageable Sort 应用动态查询分页和排序。

例7。 使用可分页,切片和排序的查询方法
Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);

第一种方法允许您通过一个 org.springframework.data.domain.Pageable 实例的查询方法,动态添加分页查询你的静态定义的。 一个 Page 知道元素的总数和每页大小

通过处理排序选项 Pageable 实例。 如果你只需要排序,只需添加一个 org.springframework.data.domain.Sort 你的方法参数。 你也可以看到,只是返回一个 List

找出你得到多少页为一个查询完全你必须触发额外的查询统计。 默认情况下这个查询将来自查询你真正触发。

2.5。 创建repository 实例

在本节中,您创建实例定义的库接口和bean定义。 这样做的一个方法是使用Spring的名称空间,支持存储库机制虽然我们一般推荐使用Java-Config样式配置。

2.5.1。 XML配置

Spring数据模块包括一个存储库元素,允许你简单地定义一个基本方案,Spring扫描给你。

示例8。 通过XML
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <repositories base-package="com.acme.repositories" />

</beans:beans>

在前面的例子中,Spring是指示扫描 com.acme.repositories 和所有的子包接口扩展 Repository 或其sub-interfaces之一。 对于发现的每个接口,注册持久性特定于技术的基础设施 FactoryBean 创建合适的代理,处理调用查询的方法。 每个bean注册在bean名称来源于接口名称,所以一个接口 UserRepository 将注册下 userRepository 。 的 base-package 属性允许通配符,您可以定义一个模式扫描包。

使用过滤器

您可能希望更细粒度的控制接口获得创建bean实例。 要做到这一点,你使用 < include-filter / > <exclude-filter /> 元素内 <repositories /> 。 元素的语义是完全等价的Spring’s 上下文名称空间。 有关详细信息,请参见 Spring参考文档 在这些元素。

例如,要排除某些接口从实例库中,您可以使用以下配置:

示例9。 过滤元素
<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

这个例子以排除所有接口 SomeRepository 被实例化。

2.5.2。 JavaConfig

存储库的基础设施也可以使用一个能找到触发 @Enable $ {商店}存储库 JavaConfig类注释。 介绍到基于java的配置Spring容器,看到参考文档。 ( 1 ]

一个示例配置,看起来是这个样子的。

10例。 基于注释的存储库配置示例
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  public EntityManagerFactory entityManagerFactory() {
    // …
  }
}
示例使用JPA-specific注释,你会改变根据你实际使用的存储模块。 这同样适用于的定义 EntityManagerFactory bean。 查阅部分覆盖,能找到配置。

2.5.3。 独立使用

您还可以使用Spring以外的容器,例如在CDI的环境中。 你还需要一些Spring库在您的类路径中,但通常你可以设置存储库以编程方式。 Spring数据模块,提供存储库支持船持久性特定于技术的RepositoryFactory,您可以使用如下。

例11。 独立使用存储库的工厂
RepositoryFactorySupport factory =  // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

2.6。 实现自定义数据存储接口

经常需要提供一个自定义实现几个库的方法。 Spring数据存储库很容易让你自定义库代码并将其集成提供通用的CRUD功能抽象和查询方法。

2.6.1。 单一存储库添加自定义行为

丰富存储库和自定义功能首先定义一个接口和一个实现自定义功能。 使用提供的库接口来扩展自定义接口。

示例12。 界面自定义库的功能
interface UserRepositoryCustom {
  public void someCustomMethod(User user);
}
示例13。 自定义库功能的实现
class UserRepositoryImpl implements UserRepositoryCustom {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}
最重要的类被发现 impl 后缀的名字相比核心存储库接口(见下文)。

实现本身并不取决于弹簧数据,可以定期Spring bean。 所以你可以使用标准的依赖注入行为注入引用其他bean JdbTemplate

例14。 改变你的基本库接口
interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom {

  // Declare query methods here
}

让你的标准库接口扩展自定义。 这样结合了CRUD和自定义功能和使它提供给客户。

配置

如果你使用名称空间配置,存储库基础设施试图自动侦测包下面的自定义实现通过扫描类我们发现存储库。 这些类需要遵循命名约定的附加名称空间elementa€™年代属性 repository-impl-postfix 发现库接口名称。 这个后缀违约 impl

15例。 配置示例
<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />

第一个配置示例将尝试查找一个类 com.acme.repository.UserRepositoryImpl 作为自定义存储库实现,而第二个示例将尝试查找 com.acme.repository.UserRepositoryFooBar

手动连接

如果您的自定义的方法实现使用基于注释的配置和自动装配,它将被视为任何其他bean。 如果您的自定义实现bean需要特殊的布线,只需声明bean和名称后约定。 基础设施将参考手动定义bean定义的名字,而不是创建一个本身。

示例16。 自定义实现
<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class="…">
  <!-- further configuration -->
</beans:bean>

2.6.2。 所有存储库添加自定义行为

前面的方法是不可行的,当你想添加一个方法到所有存储库的接口。

  1. 所有存储库添加自定义行为,首先添加一个中间接口声明的共享行为。

    示例17。 一个接口声明自定义共享的行为
    public interface MyRepository<T, ID extends Serializable>
      extends JpaRepository<T, ID> {
    
      void sharedCustomMethod(ID id);
    }
  2. 现在你的个人的接口将扩展这个中间库的接口而不是声明接口包括功能。

  3. 接下来,创建一个实现的中间接口,扩展了持久性特定于技术的基类库。 这个类将作为一个自定义的基类库代理。

    18例。 自定义库基类
    public class MyRepositoryImpl<T, ID extends Serializable>
      extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
    
      private EntityManager entityManager;
    
      // There are two constructors to choose from, either can be used.
      public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
    
        // This is the recommended method for accessing inherited class dependencies.
        this.entityManager = entityManager;
      }
    
      public void sharedCustomMethod(ID id) {
        // implementation goes here
      }
    }

    Spring的默认行为 <repositories / > 名称空间是提供下,所有接口的一个实现 基本包 。 这意味着,如果在其当前状态,实现的实例将创建MyRepositorySpring。 这当然不是理想的,因为它只是应该作为存储库和实际的存储库之间的中间层接口你想定义为每个实体。 排除一个接口,扩展库从被实例化一个存储库实例,您可以注释@NoRepositoryBean或移动它之外的配置 base-package

  4. 然后创建一个自定义存储库工厂替换默认RepositoryFactoryBean将产生一个定制的RepositoryFactory。 新仓库工厂将提供你MyRepositoryImpl存储库实现的任何接口,扩展接口,取代SimpleJpaRepository实现扩展。

    示例19。 自定义库工厂bean
    public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
      extends JpaRepositoryFactoryBean<R, T, I> {
    
      protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
    
        return new MyRepositoryFactory(entityManager);
      }
    
      private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
    
        private EntityManager entityManager;
    
        public MyRepositoryFactory(EntityManager entityManager) {
          super(entityManager);
    
          this.entityManager = entityManager;
        }
    
        protected Object getTargetRepository(RepositoryMetadata metadata) {
    
          return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager);
        }
    
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
    
          // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory
          //to check for QueryDslJpaRepository's which is out of scope.
          return MyRepository.class;
        }
      }
    }
  5. 最后,直接定制bean的工厂或者使用 factory-class Spring的名称空间的属性告诉库基础设施使用定制工厂实现。

    20例。 使用自定义的工厂名称空间
    <repositories base-package="com.acme.repository"
      factory-class="com.acme.MyRepositoryFactoryBean" />

2.7。 Spring数据扩展

这部分文档一组弹簧数据扩展,使弹簧数据在各种语境下的用法。 目前大部分的集成是针对Spring MVC。

2.7.1。 网络支持

本节包含Spring Data web支持的文档是作为Spring的实现数据共享在1.6范围内。 因为它新推出的支持改变很多事情我们保持文档的前行为 遗留web支持

Spring数据模块附带各种web支持如果模块支持库的编程模型。 网络相关的东西需要Spring MVC的jar文件的类路径中,他们中的一些人甚至提供与SpringHATEOAS的集成 ( 2 ] 。 一般来说,通过使用启用集成支持 @EnableSpringDataWebSupport 注释在JavaConfig配置类。

示例21。 支持Spring Data web支持
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration { }

@EnableSpringDataWebSupport 注释注册几个组件。 它还将检测Spring HATEOAS的类路径并注册它如果存在集成组件。

或者,如果您正在使用XML配置,注册 SpringDataWebSupport HateoasAwareSpringDataWebSupport Spring bean:

示例22。 支持Spring web支持XML数据
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />

<!-- If you're using Spring HATEOAS as well register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
基本的网络支持

上面所示的配置设置将注册几个基本组成部分:

  • 一个 DomainClassConverter 让Spring MVC解决的实例库管理域类来自请求参数或路径变量。

  • HandlerMethodArgumentResolver 实现让Spring MVC解决可分页和排序实例来自请求参数。

DomainClassConverter

DomainClassConverter 允许您使用域类型在你的Spring MVC控制器直接方法签名

示例23。 Spring MVC控制器在方法签名中使用域类型
@Controller
@RequestMapping("/users")
public class UserController {

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

正如你所看到的直接方法接收用户实例,不需要进一步的查找。 实例可以解决通过让Spring MVC path变量转换成的id类型域类,最终通过调用访问实例 findOne(...) 在存储库实例注册域类型。

目前dao层来实现 CrudRepository
HandlerMethodArgumentResolvers可分页和排序

上面的配置代码片段也注册一个 PageableHandlerMethodArgumentResolver 的一个实例 SortHandlerMethodArgumentResolver 。 注册使 Pageable Sort 被有效的控制器方法参数

例24。 使用可分页作为控制器方法的参数
@Controller
@RequestMapping("/users")
public class UserController {

  @Autowired UserRepository repository;

  @RequestMapping
  public String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

这种方法签名会导致Spring MVC尝试可分页实例来自请求参数使用默认配置如下:

表1。 Pageable请求参数

page

你想要查找的第几页

size

分页大小

sort

属性,应按格式 property,property(ASC | DESC) 。 默认排序升序从小到大ASC。 使用多个 sort 参数如果你想切换方向,例如 ?sort=firstname&sort=lastname,asc

自定义这种行为扩展 SpringDataWebConfiguration 或HATEOAS-enabled等效和覆盖 pageableResolver() sortResolver() 方法和导入您的自定义配置文件,而不是使用 @Enable 注释。

如果你需要多个 Pageable Sort 实例解决从请求(例如多个表,)您可以使用Spring’s @ qualifier 注释来区分一个从另一个。 然后请求参数必须与前缀 $ {限定符} _ 。 所以对于一个方法签名:

public String showUsers(Model model,
      @Qualifier("foo") Pageable first,
      @Qualifier("bar") Pageable second) {  }

你必须填充 foo_page bar_page 等。

默认的 Pageable 把方法相当于一个 new PageRequest(0, 20) 但是可以定制使用 @PageableDefaults 注释的 Pageable 参数。

Spring Hateoas支持可分页

SpringHATEOAS附带模型类表示 PagedResources 允许enrichting的内容 Page 实例与必要的 Page 元数据以及让客户容易的浏览这个页面的链接。 一个页面的转换 PagedResources 是通过Spring HATEOAS的实现 ResourceAssembler 接口, PagedResourcesAssembler

25例。 使用PagedResourcesAssembler作为控制器方法的参数
@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

使配置如上所示的允许 PagedResourcesAssembler 作为控制器方法参数。 调用 toResources(...) 它将导致以下:

  • Page 的内容成为 PagedResources 实例。

  • PagedResources 会得到一个 PageMetadata 实例附加填充信息形式 Page 和底层 PageRequest

  • PagedResources 得到 prev next 链接附件根据page’s 状态。 的链接将指向URI映射到调用的方法。 分页参数添加到方法将匹配的设置 PageableHandlerMethodArgumentResolver 确保链接可以得到解决。

假设我们有30 Person 在数据库实例。 你现在可以触发一个请求 GET http://localhost:8080/persons 会看到类似这样的:

{ "links" : [ { "rel" : "next",
                "href" : "http://localhost:8080/persons?page=1&size=20 }
  ],
  "content" : [
      // 20 Person instances rendered here
  ],
  "pageMetadata" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

Pageable 的请求。 这意味着,如果你改变配置,链接将自动改变。 默认情况下是调用控制器方法,但可以通过定制的url Link 作为基本的分页链接 处理 PagedResourcesAssembler.toResource(...) 方法。

2.7.2。 Repository Json

如果你使用Spring JDBC模块,你可能熟悉 DataSource 来运行SQL 语句。 存储库上可用类似的抽象水平,尽管它不使用SQL数据定义语言,因为它必须store-independent。 因此,populator将支持XML(通过Spring’s OXM抽象)和JSON(通过Jackson)定义的数据填充存储库。

假设您有一个文件 data.json 用下面的内容:

26个例子。 在JSON数据定义
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

你可以很容易地使用了populator元素填充你的存储库提供的存储库名称空间在 Spring Data Commons。 填充PersonRepository前面的数据,执行以下操作:

27个例子。 使用Jackson填充数据
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd">

  <repository:jackson-populator locations="classpath:data.json" />

</beans>

这个声明会导致 data.json 文件 阅读通过杰克逊和反序列化 ObjectMapper

JSON对象将解包数据的类型将取决于检查 _class JSON文档的属性。 基础设施最终会选择适当的存储库来处理刚刚反序列化的对象。

而使用XML来定义数据存储库填充,可以使用 unmarshaller-populator 元素。 你将它配置为使用一个XML marshallerSpringOXM提供选项。 看到 Spring参考文档 获取详细信息。

28例。 声明一个反序列化json填充器(使用JAXB)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:repository="http://www.springframework.org/schema/data/repository"
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm.xsd">

  <repository:unmarshaller-populator locations="classpath:data.json"
    unmarshaller-ref="unmarshaller" />

  <oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>

2.7.3。 Web支持

Spring MVC web绑定

鉴于您正在开发一个Spring MVC web应用程序通常需要解决域类id从url。 默认情况下,你的任务是将请求参数或URL部分转换成域类交给下层然后直接或执行业务逻辑实体。 这也许会是这样的:

@Controller
@RequestMapping("/users")
public class UserController {

  private final UserRepository userRepository;

  @Autowired
  public UserController(UserRepository userRepository) {
    Assert.notNull(repository, "Repository must not be null!");
    this.userRepository = userRepository;
  }

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") Long id, Model model) {

    // Do null check for id
    User user = userRepository.findOne(id);
    // Do null check for user

    model.addAttribute("user", user);
    return "user";
  }
}

首先声明一个库的依赖为每个控制器来查找实体分别由控制器或存储库。 查找 findOne(...) 调用。 幸运的是Spring提供了注册自定义组件意味着允许之间的转换 String 一个任意的价值类型。

PropertyEditors

Spring在3.0之前简单的Java版本 PropertyEditors 必须使用。 提供了一个集成, Spring Data DomainClassPropertyEditorRegistrar 查找所有数据存储库中注册 ApplicationContext 并注册自定义 propertyeditor 管理域类。

<bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
    <bean class="….web.bind.support.ConfigurableWebBindingInitializer">
      <property name="propertyEditorRegistrars">
        <bean class="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar" />
      </property>
    </bean>
  </property>
</bean>

如果您已经配置了Spring MVC和前面的示例一样,您可以配置您的控制器如下,这减少了很多混乱和样板。

@Controller
@RequestMapping("/users")
public class UserController {

  @RequestMapping("/{id}")
  public String showUserForm(@PathVariable("id") User user, Model model) {

    model.addAttribute("user", user);
    return "userForm";
  }
}

ConversionServiceIn Spring 3.0以及后来的 PropertyEditor 支持是取代一个新的转换基础设施,消除的缺点 PropertyEditors 和使用无状态的X,Y的转换方法。 数据现在附带一个Spring DomainClassConverter 模仿的行为 DomainClassPropertyEditorRegistrar 。 要配置,只需声明bean实例和 ConversionService 使用它的构造函数:

<mvc:annotation-driven conversion-service="conversionService" />

<bean class="org.springframework.data.repository.support.DomainClassConverter">
  <constructor-arg ref="conversionService" />
</bean>

如果您使用的是JavaConfig,您可以简单地扩展Spring MVC WebMvcConfigurationSupport FormatingConversionService 配置超类提供注入 DomainClassConverter 来创建实例。

class WebConfiguration extends WebMvcConfigurationSupport {

  // Other configuration omitted

  @Bean
  public DomainClassConverter<?> domainClassConverter() {
    return new DomainClassConverter<FormattingConversionService>(mvcConversionService());
  }
}
Web分页

在处理分页在web层通常需要编写大量的样板代码自己从请求中提取必要的元数据。 下面的例子所示的不可取的方法需要包含一个方法 httpservletrequest 参数,必须手动解析。 这个例子也省略了相应的异常处理,这将会使代码更加繁琐。

@Controller
@RequestMapping("/users")
public class UserController {

  // DI code omitted

  @RequestMapping
  public String showUsers(Model model, HttpServletRequest request) {

    int page = Integer.parseInt(request.getParameter("page"));
    int pageSize = Integer.parseInt(request.getParameter("pageSize"));

    Pageable pageable = new PageRequest(page, pageSize);

    model.addAttribute("users", userService.getUsers(pageable));
    return "users";
  }
}

控制器不应该处理从请求中提取分页信息的功能。 所以Spring数据附带 PageableHandlerMethodArgumentResolver 会为你做这项工作。 Spring MVC JavaConfig公开的支持 WebMvcConfigurationSupport 定制配置如下:

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

  @Override
  protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new PageableHandlerMethodArgumentResolver());
  }
}

如果你€™再保险在XML配置可以注册解析器如下:

<bean class="….web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  <property name="customArgumentResolvers">
    <list>
      <bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
    </list>
  </property>
</bean>

一旦完成€™已经配置了解析器与Spring MVC它允许您简化控制器下来是这样的:

@Controller
@RequestMapping("/users")
public class UserController {

  @RequestMapping
  public String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", userRepository.findAll(pageable));
    return "users";
  }
}

PageableArgumentResolver 建立一个自动解析请求参数 PageRequest 实例。 默认情况下,预计以下结构的请求参数。

表2。 由PageableHandlerMethodArgumentResolver请求参数评估

page

页面要检索、0索引和默认值为0。

size

页面的尺寸你想检索,默认为20。

sort

排序的指令格式的集合 ($ propertyname),(asc | desc)?

分页URL参数的例子

检索每页100条,第三页面,排序与电子邮件属性按升序排序的数据使用以下url参数:

?page=2&size=100&sort=email,asc

排序的数据由多个属性不同的排序顺序使用以下URL参数:

?sort=foo,asc&sort=bar,desc

如果你需要多个 Pageable 实例解决从请求(例如多个表,)您可以使用Spring's @ qualifier 注释来区分一个从另一个。 然后请求参数必须与前缀 $ {限定符} _ 。 所以对于一个方法

public String showUsers(Model model,
  @Qualifier("foo") Pageable first,
  @Qualifier("bar") Pageable second) {  }

你必须填充 foo_page bar_page 和相关的subproperties。

配置bean声明的全局缺省 PageableArgumentResolver 将使用一个 PageRequest 第一页和页面默认大小为10。 它将使用该值,如果不能获取 PageRequest 。 您可以配置一个全局bean声明。 如果你需要控制器方法默认值 Pageable 注释的方法参数 @PageableDefaults 并指定页面(通过 pageNumber ),页面大小(通过 value ), sort (的属性列表排序), sortDir (方向排序)注释属性:

public String showUsers(Model model,
  @PageableDefaults(pageNumber = 0, value = 30) Pageable pageable) {  }

参考文档

3。 JPA存储库

本章将指出专业库支持JPA。 这建立在核心存储库支持解释 Spring Data Repositories 所以确保你有一个良好的理解基本概念的解释

3.1。 介绍

3.1.1。 Spring的名称空间

Spring的JPA模块数据包含自定义的名称空间,允许存储库定义bean。 它还包含某些特性和元素属性特殊JPA。 一般可以设置使用JPA存储库 repositories 元素:

29例。 使用名称空间设置JPA存储库
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <jpa:repositories base-package="com.acme.repositories" />

</beans>

使用这个元素查找Spring中描述的数据存储库 创建存储库实例 。 除此之外它激活持久性异常为所有bean注释 @Repository 让JPA持久性提供者所抛出的异常被转换成Spring的 DataAccessException 层次结构。

自定义名称空间属性

除了默认的属性 repositories 元素JPA名称空间提供了额外的属性来获得更详细的控制存储库的设置:

表3。 定制JPA-specific存储库的属性元素

entity-manager-factory-ref

注入 EntityManagerFactory

transaction-manager-ref

事务处理 PlatformTransactionManager

注意,我们需要一个 PlatformTransactionManager bean别名为 transactionManager 如果没有显式出现 transaction-manager-ref 定义。

3.1.2。 基于注释的配置

SpringJPA数据存储库支持不能仅仅通过一个XML名称空间被激活但也通过JavaConfig使用注释。

30例。 Spring JavaConfig使用JPA数据存储库
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
class ApplicationConfig {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder.setType(EmbeddedDatabaseType.HSQL).build();
  }

  @Bean
  public EntityManagerFactory entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.acme.domain");
    factory.setDataSource(dataSource());
    factory.afterPropertiesSet();

    return factory.getObject();
  }

  @Bean
  public PlatformTransactionManager transactionManager() {

    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory());
    return txManager;
  }
}

只是显示配置类设置一个嵌入式HSQL数据库使用 EmbeddedDatabaseBuilder spring jdbc API。 然后,我们建立了一个 EntityManagerFactory 并使用Hibernate作为样本的持久性提供者。 最后使用这个组件 JpaTransactionManager 。 我们终于配置完成用JPA数据存储库 @EnableJpaRepositories 注释基本上有相同属性的XML名称空间。 如果没有配置基础包它将使用驻留在一个配置类。

3.2。 持久化实体

3.2.1。 保存实体

保存一个实体可以通过执行 CrudRepository.save(...) 方法。 它将持续或合并使用底层的JPA实体 EntityManager 。 如果实体Spring尚未持久化数据JPA将保存实体通过调用 entityManager.persist(...) 方法,否则 entityManager.merge(...) 方法将被调用。

实体状态检测策略

Spring Data JPA 提供了以下策略来检测是否一个实体是新的:

表4。 选择检测是否新的Spring数据JPA实体

id属性检查( 默认的 )

默认情况下Spring Data JPA检查给定的实体的标识符属性。 如果标识符属性 null ,那么实体将被假定为新的,否则

实现 Persistable

如果一个实体实现 可持久化 Spring,JPA将代表新检测的数据 isNew(...) 实体的方法。 看到 Javadoc 获取详细信息。

实现 EntityInformation

您可以自定义 EntityInformation 实现抽象 simplejparepository 通过创建一个子类的实现 JpaRepositoryFactory 和覆盖 getEntityInformation(...) 相应的方法。 然后您必须注册自定义的实现 JpaRepositoryFactory 作为一个Spring bean。 很少注意,这应该是必要的。 看到 Javadoc 获取详细信息。

3.3。 查询方法

3.3.1。 查询查找策略

JPA模块支持手动定义一个查询字符串或源自方法名。

查询

虽然获得查询的方法名称很方便,可能面临的局势,方法名称解析器不支持关键字的解析使得方法名会不必要地丑陋。 所以你可以使用JPA通过命名约定(见命名查询 使用JPA NamedQueries 更多信息)或而注释您的查询方法 @Query (见 使用@Query 详情)。

3.3.2。 创建查询

一般 查询方法 。一个简短的例子JPA查询方法转化为:

例31日。 从方法名称查询创建
public interface UserRepository extends Repository<User, Long> {

  List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}

我们将创建一个查询使用JPA标准API从这个但是本质上这转化为下面的查询: select u from User u where u.emailAddress = ?1 and u.lastname = ?2 ; Spring Data JPA将属性检查和遍历嵌套属性中描述 属性表达式 。 关键词支持JPA的概述和实质上的方法包含关键字翻译。

表 5. 支持关键字在方法名
Keyword Sample JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = 1?

Between

findByStartDateBetween

… where x.startDate between 1? and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age ⇐ ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> age)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

In NotIn 也采取的任何子类 Collection 作为参数以及数组或可变参数。 对于其他同样的逻辑算符的语法版本检查 查询关键字

3.3.3。 使用JPA NamedQueries

使用简单的例子 <named-query /> 元素和 @NamedQuery 注释。 这些配置元素的查询必须定义在JPA查询语言。 当然您可以使用 < named-native-query / > @NamedNativeQuery 了。 这些元素允许您定义的原生SQL查询数据库平台独立性。

XML命名查询定义

使用XML配置简单地添加必要的 <named-query /> orm.xml JPA配置文件位于 META-INF 文件夹的类路径中。 启用自动调用的命名查询通过使用一些定义的命名约定。 更多细节见下文。

32例。 XML命名查询配置
<named-query name="User.findByLastname">
  <query>select u from User u where u.lastname = ?1</query>
</named-query>

正如你所看到的查询有一个特别的名字,将被用于在运行时解决它。

注释配置

注释配置的优点是不需要编辑另一个配置文件,可能降低维护成本。 你支付需要重新编译您的域类中获益的每一个新的查询声明。

33例。 基于注释的命名查询配置
@Entity
@NamedQuery(name = "User.findByEmailAddress",
  query = "select u from User u where u.emailAddress = ?1")
public class User {

}
声明接口

允许执行这些命名查询所有您需要做的是指定的 UserRepository 如下:

例子33。 在UserRepository查询方法声明
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  User findByEmailAddress(String emailAddress);
}

Spring Data将试图解决命名查询调用这些方法,从简单的配置域类的名称,紧随其后的是方法名称由一个点。 所以这里的示例将使用上面定义的命名查询而不是试图创建一个查询从方法的名字。

3.3.4。 使用@Query

使用命名实体的申报查询是一种有效的方法,对少量的查询工作。 查询本身与执行的Java方法你可以直接使用Spring数据JPA绑定它们 @Query 注释而不是注释域类。 这可以从持久化域类特定的信息和共同部署存储库的查询接口。

查询注释定义的查询方法将优先于查询使用 @NamedQuery 或命名查询中声明 xml文件

35例。 使用@Query查询的查询方法
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

使用先进的 LIKE  expressions 这个查询执行手动定义查询机制使用@Query允许高级的定义 LIKE  在查询定义表达式。

36例子。 先进的表达式和@Query
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
}

所示的只是简单 LIKE  分隔符字符 % 识别和查询转换为一个有效的JPQL查询

@Query 通过设置注释允许执行本地查询 nativeQuery flag 为true。 注意,我们目前支持执行分页或动态排序为原生查询需要操作实际的动态查询,我们不能这样写可靠的原生SQL。

例子33。 声明一个使用@Query本地查询的查询方法
public interface UserRepository extends JpaRepository<User, Long> {

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
}

3.3.5。 使用命名参数绑定

默认情况下Spring Data JPA将使用基于位置的参数绑定如上面描述的所有例子。 这使得查询方法有点容易出错的重构有关参数的位置。 您可以使用来解决这个问题 @param 注释为方法参数的具体名称和绑定名称查询。

例38。 使用命名参数
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                 @Param("firstname") String firstname);
}

注意,方法参数是根据定义的查询中出现了。

3.3.6。 使用SpEL表达式

Spring Data JPA 1.4版本我们支持限制SpEL 模板表达式的用法通过手动定义查询 @Query 。 在查询执行这些表达式计算对一组预定义的变量。 我们支持以下变量列表中使用手动查询。

表6。 支持基于内变量SpEL查询模板
变量 使用 描述

entityName

select x from #{#entityName} x

插入 entityName 域的类型与给定的存储库。 的 entityName 解决如下:如果域类型设置的名称类型 @ entity 注释,那么它就会被使用。 否则,将使用域类型的简单的类名称。

下面的示例演示了一个用例 #{#entityName} 表达式的查询字符串,你想定义一个存储库接口查询方法有手动定义查询。 为了不需要返回实际的实体名称的查询字符串 @Query 可以使用注释 # { # entityName } 变量。

entityName 可以通过使用注解 @ entity ,通过 orm.xml 配置的不支持SpEL 表达式。

例子39。 查询方法——entityName使用SpEL表达式
@Entity
public class User {

  @Id
  @GeneratedValue
  Long id;

  String lastname;
}

public interface UserRepository extends JpaRepository<User,Long> {

  @Query("select u from #{#entityName} u where u.lastname = ?1")
  List<User> findByLastname(String lastname);
}

当然你用用户查询中直接声明,但需要您更改查询的引用 # entityName 潜在用户类的重新映射到一个不同的实体名称(例如,通过使用 @Entity(name = "MyUser"

另一个用例 # { # entityName } 在查询字符串表达式是如果你想定义一个通用库接口与专门的库接口的具体域类型。 为了不重复的定义自定义查询具体的接口上的方法可以使用实体名称查询字符串的表达式 @Query 在通用库接口注释。

40例。 在库查询方法使用SpEL表达式——entityName与继承
@MappedSuperclass
public abstract class AbstractMappedType {
  
  String attribute
}

@Entity
public class ConcreteType extends AbstractMappedType {  }

@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
  extends Repository<T, Long> {

  @Query("select t from #{#entityName} t where t.attribute = ?1")
  List<T> findAllByAttribute(String attribute);
}

public interface ConcreteRepository
  extends MappedTypeRepository<ConcreteType> {  }

在示例接口 MappedTypeRepository 是一些domian类型的公共父接口扩展 AbstractMappedType 。 它还定义了通用的方法 findAllByAttribute(...) 可用于专门的库接口的实例。 如果你现在调用 findByAllAttribute(...) ConcreteRepository 正在执行的查询 select t from ConcreteType t where t.attribute = ?1.

3.3.7。 修改查询

所有上面的部分描述了如何声明查询访问给定实体或实体的集合。 自定义实现数据存储库 。 作为全面的定制功能,这种方法是可行的,你可以实现的执行修改查询,实际上只需要注释参数绑定 @Modifying :

例子41。 声明操作查询
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

这将触发查询注释方法更新查询,而不是选择一个。 随着 EntityManager 可能包含过时的实体修改查询的执行之后,我们不会清除(见的JavaDoc EntityManager.clear() )因为这将有效地放弃所有non-flushed变化仍然悬而未决 EntityManager 。 如果你希望 EntityManager 您可以设置自动清除 @Modifying annotationa€™ clearAutomatically 属性 true

3.3.8。 应用查询提示

JPA查询提示应用于存储库中声明的查询接口可以使用 @QueryHints 注释。 它需要一个JPA数组 @QueryHint 注释加上一个布尔禁用额外计算查询应用分页。

42例。 用QueryHints的方法
public interface UserRepository extends Repository<User, Long> {

  @QueryHints(value = { @QueryHint(name = "name", value = "value")},
              forCounting = false)
  Page<User> findByLastname(String lastname, Pageable pageable);
}

只是显示声明将应用配置 @QueryHint 实际查询但是省略应用统计查询触发计算总页数。

3.3.9。 配置获取LoadGraphs

JPA 2.1规范引入了支持特定的获取,LoadGraphs,我们还支持通过 @EntityGraph 注释可以引用一个 @NamedEntityGraph 定义,可以带注释的一个实体,用于配置获取计划产生的查询。 的类型(Fetch / Load)获取可以通过配置 type 属性 @EntityGraph 注释。 请看看JPA 2.1规范3.7.4供进一步参考。

43例。 在一个实体定义一个命名实体图。
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
  attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {

  // default fetch mode is lazy.
  @ManyToMany
  List<GroupMember> members = new ArrayList<GroupMember>();

  
}
例子44。 引用命名实体图定义的存储库的查询方法。
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {

  @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
  GroupInfo getByGroupName(String name);

}

3.4。 存储过程

JPA 2.1规范引入了支持通过JPA标准查询API调用存储过程。 我们介绍了 @Procedure 注释声明存储过程的元数据存储库的方法。

45例子。 的定义pus1inout HSQL数据库的过程。
/;
DROP procedure IF EXISTS plus1inout
/;
CREATE procedure plus1inout (IN arg int, OUT res int)
BEGIN ATOMIC
 set res = arg ` 1;
END
/;

元数据存储过程可以通过配置 NamedStoredProcedureQuery 在一个实体类型注释。

46个例子。 StoredProcedure元数据定义一个实体。
@Entity
@NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = {
  @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class),
  @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) })
public class User {}

存储过程可以从存储库引用以多种方式方法。 定义的存储过程被称为可以直接通过 value procedureName 的属性 @Procedure 注释或间接通过 name 属性。 如果没有配置名称的名称用作后备存储库的方法。

47个例子。 显式地引用映射过程名称“plus1inout”数据库。
@Procedure("plus1inout")
Integer explicitlyNamedPlus1inout(Integer arg);
48例子。 引用隐式映射过程与名称“plus1inout”数据库 procedureName 别名。
@Procedure(procedureName = "plus1inout")
Integer plus1inout(Integer arg);
49个例子。 “用户显式地引用映射命名存储过程。 在EntityManager plus1IO”。
@Procedure(name = "User.plus1IO")
Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
50例。 引用“用户隐式映射命名存储过程。 通过方法名plus1”EntityManager。
@Procedure
Integer plus1(@Param("arg") Integer arg);

3.5。 规范

JPA 2引入了一个标准的API,可以通过编程方式构建查询。 写一个 criteria 你真的为域类定义一个查询的where子句。 拿回另一个步骤这些标准可以被视为谓词描述的实体JPA API标准约束。

Spring数据JPA规范的概念从Eric Evans的书“Domain Driven Design”,遵循同样的语义和提供一个API来定义这些规范使用JPA标准API。 支持与规范可以扩展您的存储库接口 JpaSpecificationExecutor 接口:

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
 
}

额外的接口进行方法允许您以各种方式执行规范。 例如, findAll 方法将返回所有实体匹配的规格:

List<T> findAll(Specification<T> spec);

Specification 接口定义如下:

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder);
}

好吧,那么的典型用例是什么呢? 规格可以很容易地用来构建一个可扩展的谓词集上的一个实体,然后可以组合使用 JpaRepository 不需要声明一个查询(方法)对每一个需要的组合

51例。 一个客户的规格
public class CustomerSpecs {

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         LocalDate date = new LocalDate().minusYears(2);
         return builder.lessThan(root.get(_Customer.createdAt), date);
      }
    };
  }

  public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
    return new Specification<Customer>() {
      public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder) {

         // build query here
      }
    };
  }
}

无可否认的样板改进余地(有望减少Java 8闭包),但客户端变得更好你会看到如下。 的 _Customer 类型是一种元模型生成JPA使用元模型生成器(参见 例如Hibernate implementationa€™文档 )。 因此,表达式 _Customer.createdAt 是asuming 客户 有一个 createdAt 属性的类型 Date 。 除此之外我们表达了一些业务需求抽象级别标准,创建可执行的 Specifications(规范) 。 所以客户可能使用 Specification 如下:

例子52。 使用一个简单的规范
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

好吧,为什么不简单地创建一个查询这种数据访问吗? 你€™是正确的。 使用单一 Specification 没有获得很多好处在普通查询声明。 规范的力量真正闪光的时候你把他们创建新的 Specifications 对象。 你可以做到这一点通过 Specifications 助手类我们提供构建表达式如下:

53个例子。 结合规范
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
  where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));

正如您可以看到的, Specifications 提供了一些胶水代码链和结合的方法 Specification 实例。 因此扩展数据访问层只是一个创建新的问题 Specification 已经存在的并结合实现的。

3.6。 事务性

CRUD方法默认存储库实例事务。 读操作的事务配置 readOnly 标志被设置为true,所有其他人都配置了一个平原 @Transactional 这样默认事务配置应用。 详情见的JavaDoc CrudRepository 。 如果你需要调整事务配置存储库中的声明的方法的简单地重新定义存储库接口中的方法如下:

例子54。 自定义事务配置CRUD
public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

这将导致 findAll() 方法超时时间为10秒,没有 readOnly 配置

另一个可能改变事务性行为是使用facade或服务实现,通常包括多个存储库。 其目的是为non-CRUD定义事务边界操作:

例子55。 使用外观定义多个存储库调用的事务
@Service
class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  @Autowired
  public UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
}

这将会导致调用 addRoleToAllUsers(...) 运行在一个事务, 事务的配置存储库将被忽视然后作为外部事务的配置决定了实际使用。 请注意,您必须激活 <tx:annotation-driven /> 或使用 @EnableTransactionManagement 明确基于注释的配置包扫描。 上面的示例假设您正在使用组件扫描。

3.6.1。 事务性查询方法

让你的查询方法是事务的简单使用 @ transactional 在您定义存储库接口。

例子56。 使用@ transactional查询方法
@Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常您会希望只读的标志设置为true,因为大多数只会读取数据查询的方法。 相比之下, deleteInactiveUsers() 利用 @Modifying 注释和覆盖事务配置。 因此该方法将与执行 readOnly 标志设置为 false

这不会充当检查你不触发操作查询(尽管一些数据库拒绝 INSERT UPDATE 语句在一个只读事务)。 readOnly 而不是传播作为提示为性能优化底层JDBC驱动程序。 此外,Spring将执行一些优化底层JPA提供者。 如在使用Hibernate flush模式设置为叠加 NEVER 当您配置一个事务 ReadOnly 导致Hibernate跳过检查(明显改善大对象)。

3.7。 锁定

指定要使用的锁定模式 @Lock 注释可用于查询方法:

例57。 定义锁元数据查询方法
interface UserRepository extends Repository<User, Long> {

  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

这种方法声明会导致查询被触发的装备了 LockModeType.READ 。 您还可以定义锁定为CRUD方法重新定义存储库中的接口和添加 @Lock 注释:

例58。 在CRUD方法定义锁元数据
interface UserRepository extends Repository<User, Long> {

  // Redeclaration of a CRUD method
  @Lock(LockModeType.READ);
  List<User> findAll();
}

3.8。 审计(用处不大)

3.8.1。 基础知识

Spring数据提供了复杂的支持透明跟踪创建或改变一个实体,这个发生的时间点。 受益于这些功能,你必须使你的实体类审计可以定义元数据,使用注释或实现一个接口。

基于注释的审计元数据

我们提供 @CreatedBy , @LastModifiedBy 捕获的用户创建或修改实体以及 @CreatedDate @LastModifiedDate 捕捉到了这个时间点发生。

例子59。 一个审计实体
class Customer {

  @CreatedBy
  private User user;

  @CreatedDate
  private DateTime createdDate;

  // … further properties omitted
}

正如您可以看到的,注释可以有选择性地应用,这取决于信息百度€™想捕获。 注释捕获的时间点可用于JodaTimes类型的属性 datetime 、遗留Java Date Calendar JDK8 data/time以及类型 long / Long 

基于接口的审计元数据

如果你夫人€™t想使用注解来定义审计元数据可以让你的域类实现 可审计的 接口。 它使所有的审计属性的setter方法。

还有€™年代也是一个方便的基类 AbstractAuditable 您可以扩展,以避免需要手动实现接口的方法。 请注意,这增加了您的域类的耦合弹簧数据可能是你要避免的东西。 通常基于注释定义审计元数据的方法是首选的,因为它是侵入性较小和更灵活。

AuditorAware

如果你使用 @CreatedBy @LastModifiedBy ,审计基础设施需要意识到当前的。 为此,我们提供了一个 AuditorAware < T > SPI接口,您必须实现告诉当前用户的基础设施或系统与应用程序交互。 泛型类型 t 定义什么类型的属性的注释 @CreatedBy @LastModifiedBy 必须的。

Herea€™年代一个示例实现接口的使用Spring Securitya€™年代 身份验证 对象:

60例。 AuditorAware基于Spring安全的实现
class SpringSecurityAuditorAware implements AuditorAware<User> {

  public User getCurrentAuditor() {

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    if (authentication == null || !authentication.isAuthenticated()) {
      return null;
    }

    return ((MyUserDetails) authentication.getPrincipal()).getUser();
  }
}

实现访问 身份验证 对象由Spring Security和查找提供自定义 UserDetails 实例,您已经创建了 UserDetailsService 实现。 这里我们€™再保险假设你暴露的域用户通过 UserDetails 实现,但你也可以从任何地方查的基础上 身份验证 发现。

3.9。 JPA审计

3.9.1。 一般审计配置

Spring数据JPA附带一个实体侦听器,可用于触发捕获审计信息。 所以首先你必须注册 AuditingEntityListener 在你 xml文件 用于所有实体的持久上下文:

注意,审计功能需要 spring-aspects.jar 在类路径中。

61年的例子。 审计配置xml文件
<persistence-unit-metadata>
  <persistence-unit-defaults>
    <entity-listeners>
      <entity-listener class="….data.jpa.domain.support.AuditingEntityListener" />
    </entity-listeners>
  </persistence-unit-defaults>
</persistence-unit-metadata>

现在启动审计功能只是一个Spring数据添加JPA 审计 名称空间元素到您的配置:

62年的例子。 激活使用XML配置审计
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />

JPA 1.5作为Spring的数据,审计可以通过注释的configuration类@EnableJpaAuditing注释。

例子63。 激活通过Java配置审计
@Configuration
@EnableJpaAuditing
class Config {

  @Bean
  public AuditorAware<AuditableUser> auditorProvider() {
    return new AuditorAwareImpl();
  }
}

如果你让一个AuditorAware类型的bean ApplicationContext,审计基础设施将自动把它捡起来,用它来确定当前用户设置域类型。 如果您有多个实现在ApplicationContext注册,您可以选择使用显式地设置 auditorAwareRef @EnableJpaAuditing属性。

4。 杂项

4.1。 合并持久性单元

Spring支持拥有多个持久性单元的盒子。 但是,有时候您可能想要模块化应用程序但仍确保所有这些模块运行在运行时在一个持久化单元。 Spring数据JPA提供了一个 PersistenceUnitManager 实现自动合并持久性单元基于他们的名字。

例子64。 使用MergingPersistenceUnitmanager
<bean class="….LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager">
    <bean class="….MergingPersistenceUnitManager" />
  </property>
</bean>

4.1.1。 类路径扫描和JPA @ entity类映射文件

一个普通的JPA设置要求所有注释中列出的映射实体类 orm.xml 。 同样适用于XML映射文件。 SpringClasspathScanningPersistenceUnitPostProcessor JPA提供数据,基本包和可选配置映射文件名模式。 然后它会扫描给包类和@ entity注释或@MappedSuperclass并加载配置文件匹配的文件名模式和手JPA配置。 后处理程序必须配置如下:

例子65。 使用ClasspathScanningPersistenceUnitPostProcessor
<bean class="….LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitPostProcessors">
    <list>
      <bean class="org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor">
        <constructor-arg value="com.acme.domain" />
        <property name="mappingFileNamePattern" value="**/*Mapping.xml" />
      </bean>
    </list>
  </property>
</bean>

Spring 3.1的包扫描可以直接在LocalContainerEntityManagerFactoryBean配置启用类路径扫描实体类。 看到 Javadoc 获取详细信息。

4.2。 CDI集成

实例库的接口通常是由一个容器,Spring是最自然的选择Spring在处理数据。 还有复杂的支持可以轻松设置Spring创建bean实例记录 创建存储库实例 。 Spring的1.1.0版数据JPA附带一个定制的CDI扩展,允许使用存储库在CDI环境中抽象。 扩展JAR的一部分所以激活它所有您需要做的是把弹簧数据JPA JAR到您的类路径中。

现在,您可以设置通过实现CDI生产商的基础设施 EntityManagerFactory EntityManager :

class EntityManagerFactoryProducer {

  @Produces
  @ApplicationScoped
  public EntityManagerFactory createEntityManagerFactory() {
    return Persistence.createEntityManagerFactory("my-presistence-unit");
  }

  public void close(@Disposes EntityManagerFactory entityManagerFactory) {
    entityManagerFactory.close();
  }

  @Produces
  @RequestScoped
  public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) {
    return entityManagerFactory.createEntityManager();
  }

  public void close(@Disposes EntityManager entityManager) {
    entityManager.close();
  }
}

必要的设置可以取决于你运行在JavaEE环境。 这也可能只是足以重新定义 EntityManager 当CDI bean如下:

class CdiConfig {

  @Produces
  @RequestScoped
  @PersistenceContext
  public EntityManager entityManager;
}

在本例中,容器必须能够创建JPA entitymanager 本身。 所有的配置JPA EntityManager CDI bean。

Spring数据JPA CDI可用的扩展将捡起所有entitymanager作为春季CDI bean并创建一个代理数据存储库存储库类型的bean时要求的容器。 因此获得一个Spring的实例数据存储库是一个声明一个问题 @ inject 属性:

class RepositoryClient {

  @Inject
  PersonRepository repository;

  public void businessMethod() {
    List<Person> people = repository.findAll();
  }
}

附录

附录A:名称空间引用

<repositories/>元素

<repositories/ > 元素触发弹簧数据仓库基础设施的设置。 最重要的属性 base-package 它定义了Spring包扫描数据存储库的接口。 ( 3 ]

表7。 属性
Name 描述

base-package

定义要使用的包存储库接口扩展*存储库扫描(实际接口是由特定的弹簧数据模块)在汽车检测模式。 所有包下面配置的方案将被扫描,。 通配符是允许的。

repository-impl-postfix

定义了后缀自动检测自定义库的实现。 类的名字以配置的后缀结尾将视为候选人。 默认为 impl

query-lookup-strategy

确定要使用的策略来创建finder查询。 看到 查询查找策略 获取详细信息。 默认为 create-if-not-found

named-queries-location

定义的位置寻找一个属性文件包含外部定义的查询。

consider-nested-repositories

控制嵌套库接口定义是否应该被考虑。 默认为

附录B:populator将名称空间引用

< populator / >元素

< populator / > 元素允许填充通过弹簧数据存储库数据存储基础设施。 ( 4 ]

表8。 属性
Name 描述

locations

在哪里找到的文件读取对象存储库应填充。

附录C:库查询关键词

支持查询关键字

下表列出了关键字一般弹簧数据存储库支持的查询派生机制。 然而,咨询准确的找到文档支持的关键字列表,因为这里列出一些可能不支持在一个特定的商店。

Table 9. Query keywords
逻辑关键字 关键字表达式

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

Containing, IsContaining, Contains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWith, IsEndingWith, EndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

Is, Equals, (or no keyword)

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

Regex, MatchesRegex, Matches

STARTING_WITH

StartingWith, IsStartingWith, StartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

附录D:常见问题

常见的

  1. 想获得更详细的日志记录信息的方式被称为内部 JpaRepository ,例如如何获得呢?

    你可以利用 CustomizableTraceInterceptor

    <bean id="customizableTraceInterceptor" class="
      org.springframework.aop.interceptor.CustomizableTraceInterceptor">
      <property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
      <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
    </bean>
    
    <aop:config>
      <aop:advisor advice-ref="customizableTraceInterceptor"
        pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/>
    </aop:config>

基础设施

  1. 现在我实现了一个基于库层 hibernatedaosupport 。 我创建了一个 sessionfactory 通过使用Springa€™ AnnotationSessionFactoryBean 。 我怎么得到Spring Data repositories在这种环境下工作吗?

    你必须更换 AnnotationSessionFactoryBean HibernateJpaSessionFactoryBean 如下:

    例子66。 查找从HibernateEntityManagerFactory SessionFactory
    <bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

审计

  1. 我想使用Spring JPA数据审计功能,但我的数据库已经建立在实体设置修改和创建日期。 如何预防spring以编程方式设置日期的数据。

    只使用 set-dates 的属性 auditing 名称空间元素为false。

附录E:术语表

AOP

面向方面的编程

Commons DBCP

共享数据库连接池——Apache基金会的图书馆提供池数据源接口的实现。

Crud

Create, Read, Update, Delete 创建、读取、更新、删除,基本的持久化操作

DAO

数据访问对象模式分离持久化逻辑从对象持久化

依赖注入

模式来手componenta€™年代依赖组件以外,释放组件查找依赖本身。 更多信息见 http://en.wikipedia.org/wiki/Dependency_Injection

EclipseLink

对象关系映射器实现JPA - http://www.eclipselink.org

Hibernate

对象关系映射器实现JPA - http://www.hibernate.org

JPA

Java Persistence API

Spring

Java应用程序框架, http://projects.spring.io/spring-framework


1 在SpringJavaConfig参考文档
2 。 SpringHATEOAS - https://github.com/SpringSource/spring-hateoas
3 。 看到 XML配置
4 。 看到 XML配置