Spring Data Jpa 参考文档(一)
1.使用spring data repositorys
spring data repository abstraction节省了大量的数据访问层实现持久化的代码。
1.1核心概念
Repository是核心接口,接口需要实体类型和实体类id的类型参数。CrudRepository接口继承Repository接口,并提供了对实体类的CRUD功能。实例如下:
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
T findOne(ID primaryKey);
Iterable<T> findAll();
Long count();
void delete(T entity);
boolean exists(ID primaryKey);
// … more functionality omitted.
}
PagingAndSortingRepository接口继承了CrudRepository接口,提供了对实体的分页功能。代码如下:
public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
分页实现代码如下:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));
1.2 查询方法
spring data 完成一个查询需要以下4个过程:
(1)建一个继承Repository或Repository的子类的一个接口,并绑定实体类。
public interface PersonRepository extends Repository<User, Long> { … }
(2)在接口中定义查询方法。
List<Person> findByLastname(String lastname);
(3)在sping配置文件中创建接口的代理实例。
<?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>
(4)获得接口实例并执行查询方法。
public class SomeClient {
@Autowired
private PersonRepository repository;
public void doSomething() {
List<Person> persons = repository.findByLastname("Matthews");
}
}
以下是对4个过程的说明:
定义接口
第一步定义一个指定实体类的接口,这个接口必须继承Repository,并且要输入实体类和ID类型。如果要提供实体的CRUD操作,那么可以用CrudRepository替代Repository接口。
通常,定义的repository接口都继承Repository, CrudRepository or PagingAndSortingRepository接口。有时如果不想继承这些spring data接口,那么也可以给接口注解@RepositoryDefinition来实现。继承CrudRepository 会包含对实体的完整管理操作,如果只想选择性的提供某些方法,那么可以在CrudRepository接口中拷贝想要提供的操作方法放到定义的接口中。
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);
}
在这第一步为实体类定义了一个基本接口,并且提供了findOne(…) 和 save(…)方法,这些方法会通过匹配CrudRepository接口的签名方法并传递到实现类中。因此,接口UserRepository将包含保存用户、通用id得到唯一用户和通过邮件地址查询用户的功能实现。
定义查询方法
Repository代理能够通过方法名来生成查询语句。它可以直接通过方法名生成,也可以利用策略构建查询语句。
在命名空间中通过query-lookup-strategy属性来配置策略。有些策略在特殊的存储环境下不支持。
CREAT —— create是根据接口方法名来构建查询语句,方法是首先删掉一些固定的常用的前缀,然后在解析剩下的部分。
USE_DECLARED_QUERY——它会查找已经定义的查询语句,如果没发现会抛出异常。这个查询语句是通过某处的注解或其他方法定义的。根据指定的规范文档来查找可用选项,如果在方法被调用时没有找到定义的查询,那么会出现错误。
CREATE_IF_NOT_FOUND (default)——CREATE_IF_NOT_FOUND 结合了 CREATE 和USE_DECLARED_QUERY。首先查找已定义的查询,如果没有找到,会创建一个基于方法名称的查询。这个策略是默认的,如果没有设置的话它将起作用。
Query的创建
spring data 的query构建机制对实体的查询非常有用,它会去掉方法名中的find…By、read…By和 get…By 前缀,然后再解析剩余的部分。方法名中可以加入distinct来表明创建的查询语句需要distinct约束,方法名中的第一个by是查询语句的开始,也可以定义属性的查询条件,并且可以使用and和or来连接多个条件。
public interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// 查询中可带distinct标识
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// 属性忽略大小写
List<Person> findByLastnameIgnoreCase(String lastname);
// 所有的属性忽略大小写
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// 带order by排序的查询语句
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
方法名解析的结果是与持久化储存相关的,但是,还要注意以下几个问题:
- 在多个属性组根据合查询条件时,可以使用and和or连接。另外,还支持Between、LessThan、GreaterThan、Like这样的运算符。这些运算的使用取决于数据库,因此要参考相关文档。
- 方法名的解析支持为各个属性设置IgnoreCase,例如,findByLastnameIgnoreCase(…)或者findByLastnameAndFirstnameAllIgnoreCase(…)。但是,IgnoreCase也可能跟数据库有关系,需要参考相关的文档。
- 在方法名中直接指定order by (desc、asc)来支持排序,另外还支持动态排序,见参数绑定一节。
属性表达式
前面的例子,属性的表达式都是引用了实体本身的属性。当然,还可以引用属性嵌套的属性。例如,Persons类有一个属性Addresses,Addresses里包含一个ZipCodes.属性,那么方法名应该是这样
List<Person> findByAddressZipCode(ZipCode zipCode);
创建属性的遍历如同x.address.zipCode。解析算法是首先取出作为实体属性的完整部分(AddressZipCode),检查这部分是否是实体类的属性,如果是那么解析成功,否则,将这个部分按照驼峰式从右往左分割为两部分(头和尾),将分割的两部分跟实体属性进行匹配,如:AddressZipCode分为AddressZip 和 Code。如果解析器在属性中匹配了去掉尾的头,那么将剩下的尾部再进行同样方式的解析。如果第一次分割不能匹配,解析器会向左移动分割点(Address,ZipCode),并继续解析。
虽然这运用在大多数情况下,但也有可能将属性解析错。假设Person类也有一个addressZip属性,解析器将匹配第一次的分割,其实选择了一个错误的属性并且最终失败(AddressZip中没有code)。要解决这个有歧义性的错误,可以在方法名中利用”_“来手动定义分割点,因此方法名将会像这样:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
特殊参数绑定
以上例子中展示了在查询语句中绑定简单的参数。除此之外,数据访问层还使用一些特殊的类型如Pageable 和、Sort来实现分页和动态排序查询。
Page<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的实例添加分页到已定义的查询语句中,在Pageable实例中绑定了排序项。如果只要排序,可以在参数中仅添加org.springframework.data.domain.Sort参数,你可以看到,返回了一个List对象。在Page实例所需附加元数据不被创建的情况下,只做了一个简单的查询。
创建Repository实例
创建已定义的Repository接口的实例,最简单的方式是通过使用spring命名空间。
基于XML配置
Spring Data模块包含了一个repositories元素,这个元素允许定义spring扫描的包路径。
<?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>
在这个例子中,"base-package"指定了spring要扫描的包路径com.acme.repositories及全部子包,在这些包下的接口必须继承Repository接口或者是Repository的子接口。基础设施层注册了FactoryBean来代理处理查询方法接口的调用。每个注册的bean名称来源于各接口的名称,因此,UserRepository的接口名称将被注册为userRepository。base-package属性允许采用通配符作为包扫描的格式。
使用过滤器
在<repositories />中可以利用<include-filter />和<exclude-filter /> 元素来更细粒度的控制接口的实例化。例如,在实例化的时候可以排除一些确定的接口,配置如下:
<repositories base-package="com.acme.repositories">
<context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>
这个例子在接口实例化的时候不包含以SomeRepository结尾的接口。
基于Java的配置
在javaConfig类上可以通过@Enable${store}Repositories注解来实现。样例如下:
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory() {
// …
}
}
独立用法
在spring容器之外使用。在项目的类路径下引入spring的包,以编程的方式来来设置Repsitories。SpringData模块中提供了持久类RepositoryFactory来实现,代码如下:
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
1.3自定义SpringData Repositories的实现
spring data允许自定义实现数据访问的存取方法,并且可以集成常用CRUD和查询功能。
添加自定义的行为
首先为数据存储建立一个自定义功能的接口及实现。
interface UserRepositoryCustom {
public void someCustomMethod(User user);
}
class UserRepositoryImpl implements UserRepositoryCustom {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
在标准接口中继承自定义的接口:
public interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom {
// Declare query methods here
}
配置
如果使用命名空间配置,数据基础框架会在指定的包下自动搜索自定义的实现类,这些类的名称必须满足在repository-impl-postfix元素中指定的命名约定,这个后缀默认配置为Impl。
<repositories base-package="com.acme.repository" />
<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />
第一个配置例子会查找com.acme.repository.UserRepositoryImpl,第二个例子查找com.acme.repository.UserRepositoryFooBar。
手动装配
前面的例子如果在基于注解并且只有自动适配的情况下可以很好的运行,因为它会在任意的spring Bean中匹配。如果自定义的实现需要手动装配,那需要声明这个bean并且要以刚刚描述的约定去命名。基础框架会引用手动定义的基于名称的bean,而不是创建自身。
<repositories base-package="com.acme.repository" />
<beans:bean id="userRepositoryImpl" class="…">
<!-- further configuration -->
</beans:bean>
为所有的储存添加自定义行为
当想添加一个自定义的行为在所有储存接口时,前面的例子无法做到。
-
定义一个声明共享行为的中间接口。
现在需要各自的储存接口继承这个中间接口,而不是Repository接口。public interface MyRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { void sharedCustomMethod(ID id); }
-
接下来,创建一个实现了中间接口并且继承了规范库中的基类,这个类会作为Repository代理的自定义基类来执行。
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 Data JPA动态条件查询的写法
-
Spring Data JPA使用Sort进行排序(Using Sort)
-
详解Spring Data JPA系列之投影(Projection)的用法
-
详解Spring Data JPA使用@Query注解(Using @Query)
-
Spring Data JPA实现分页Pageable的实例代码
-
spring data jpa使用详解(推荐)
-
Spring Data JPA例子代码[基于Spring Boot、Mysql]
-
Spring-Data-JPA整合MySQL和配置的方法
-
在Spring Boot中使用Spring-data-jpa实现分页查询
-
Spring Boot JPA如何把ORM统一起来