Spring Data
一.概述
1.Spring Data 概述
Spring Data:
Spring 的一个子项目。用于简化数据库访问,支持 NoSQL 和关系数据存储。
其主要目标是使数据库的访问变得方便快捷。
SpringData 项目所支持的 NoSQL 存储:
MongoDB(文档数据库)
Neo4j(图形数据库)
Redis(键/值存储)
HBase(列族数据库)
SpringData 项目所支持的关系数据存储技术:
JDBC
JPA
2.JPA Spring Data 概述
JPA Spring Data :
致力于减少数据访问层(DAO)的开发量。开发者唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA.
二.Spring Data JPA HelloWorld
使用 Spring Data JPA 进行持久层开发需要的四个步骤:
1.配置 Spring 整合 JPA
2.在 Spring 配置文件中配置 Spring Data,让 Spring 为声明的接口创建代理对象。配置了<jpa:repositories>后,Spring
初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将
代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
3.声明持久层的接口,该接口继承 Repository, Repository 是一个标记型接口,它不包含任何方法,如必要,Spring Data 可实现
Repository 其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
4.在接口中声明需要的方法。Spring Data 将根据给定的策略来为其生成实现代码。
三.Repository 接口
Repository 接口:
1.Repository 是一个空接口,即是一个标记接口。
2.若定义的接口继承了 Repository,则该接口会被 IOC 容器识别为一个 Repository Bean.
纳入到IOC容器中。进而可以在该接口中定义满足一定规范的方法。
3.实际上,也可以通过注解的方式 @RepositoryDefinition 注解来替代继承 Repository 接口
domainClass=类.class
idClass=Integer.class
@RepositoryDefinition(domainClass=Person.class,idClass=Integer.class)
1.Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法
public interface Repository<T,ID extends Serializable>{}
2.Spring Data 可以让我们之定义接口,只要遵循 Spring Data 的规范,就无需实现类。
3.与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClass 和 idClass 属性。
Repository 子接口
基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。他们的继承关系如下:
Repository : 仅仅是一个标识,表明任何继承它的均为仓库接口类。
CrudRepository : 继承 Repository,实现了一组 CRUD 相关的方法
PagingAndSortingRepository : 继承 CrudRepository, 实现了一组分页排序相关的方法
JpaRepository : 继承 PagingAndSortingRepository,实现了一组 JPA 规范相关的方法
自定义的 XxxRepository 需要继承 JpaRepository,这样的 XxxRepository 接口就具备了
通用的数据访问控制层的能力。
JpaSpecificationExecutor : 不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法
四.Spring Data 查询方法定义规范
在 Repository 子接口中声明方法
1.不是随便声明的。而需要符合一定的规范。
规范:
简单条件查询
1.简单条件查询:查询某一个实体类或集合
2.按照 Spring Data 的规范,查询方法以 find | read | get 开头。
涉及条件查询时,条件的属性用条件关键字链接,要注意的是:条件属性是以首字母大写。
例如:定义一个 Entity 实体类
class User {
private String firstName;
private String lastName;
}
使用 And 条件连接时,应这样写:
findByLastNameAndFirstName(String lastName, String firstName);
条件的属性名称与个数要与参数的位置与个数一一对应。
3.支持级联查询。若当前类有符合条件的属性,则优先使用,而不是用级联属性。
若需要使用级联属性,则属性之间使用 _ 进行连接。
例如:
List<Person> getByLastNameStartingWithAndIdLessThan(String lastName, Integer id);
五.SpringData 支持的关键字
直接在接口中定义查询方法,如果是符合规范的,可以不用写实现,目前支持的关键字写法如下:
keyword |
sample |
JPQL snippet |
And |
findByLastnameAndFirstname |
…where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
…where x.lastname = ?1 or x.firstname = ?2 |
Between |
findByStartDateBetween |
…where x.startDate between ?1 and ?2 |
LessThan |
findByAgeLessThan |
…where x.age < ?1 |
GreaterThan |
findByGreaterThan |
…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 is not null |
六.@Query 注解
使用 @Query 注解可以自定义 JPQL 语句以实现更灵活的查询。
例如:
@Query("SELECT p FROM Person p WHERE p.id = (SELECT max(p2.id) FROM Person p2)")
Person getMaxIdPerson();
为 @Query 注解传递参数的方式1:使用占位符。
例如:
@Query("SELECT p FROM Person p WHERE p.lastName = ?1 and p.email = ?2")
List<Person> testQueryAnnotationParams1(String lastName, String email);
为 @Query 注解传递参数的方式2:命名参数的方式(与参数的顺序无太大关系)
例如:
@Query("SELECT p FROM Person p WHERE p.lastName = :lastName AND p.email = :email")
List<Person> testQueryAnnotationParams2(@Param("email") String email, @Param("lastName") String lastName);
LIKE: SpringData 允许在占位符上添加 %%。
方式一:
@Query("SELECT p FROM Person p WHERE p.lastName LIKE ?1 OR p.email LIKE ?2")
List<Person> testQueryAnnotationLikeParam(String lastName, String email);
传入的参数:
List<Person> persons = personRepository.testQueryAnnotationLikeParam("%A%","%bb%");
方式二:
或者
@Query("SELECT p FROM Person p WHERE p.lastName LIKE %?1% OR p.email LIKE %?2%")
List<Person> testQueryAnnotationLikeParam(String lastName, String email);
传入的参数:
List<Person> persons = personRepository.testQueryAnnotationLikeParam("A","bb");
方式三:
或者
@Query("SELECT p FROM Person p WHERE p.lastName LIKE %:lastName% OR p.email LIKE %:email%")
List<Person> testQueryAnnotationLikeParam(@Param("email") String email, @Param("lastName") String lastName);
传入的参数:
List<Person> persons = personRepository.testQueryAnnotationLikeParam("bb","A");
设置 nativeQuery=true 即可以使用原生的 SQL 查询。
@Query(value="SELECT count(id) FROM jpa_persons", nativeQuery=true)
long getTotalCount();
七.@Modifying 注解和事务
添加在 Service 层。在 application.xml 文件中配置自动扫描。
@Modifying
@Query("UPDATE Person p SET p.email = :email WHERE id = ")
事务:
1.Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。
2.对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional 声明。
3.进行多个 Repository 操作时,也应该使他们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层,因此,
需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。
八.CrudRepository 接口
九.PagingAndSortingRepository 接口
public void testPagingAndSortingRepository() {
int pageNo = 3;
int pageSize = 5;
PageRequest pageRequest = new PageRequest(pageNo, pageSize);
Page<Person> page = personRespository.findAll(pageRequest);
排序相关.Sort 封装了排序的信息
Order order1 = new Order(Direction.DESC, "id");
Order order2 = new Order(Direction.ASC,"email");
Sort sort = new Sort(order1, order2);
PageRequest pageable = new PageRequest(pageNo,pageSize,sort);
}
十.JpaRepository 接口
十一.JpaSpecificationExecutor 接口
JpaSpecificationExecutor :
1.不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法
2.Specification : 封装 JPA Criteria 查询条件。通常使用匿名内部类的方式来创建该接口的对象。
十二.自定义 Repository 方法
1.为某一个Repository 上添加自定义的方法
步骤:
1.定义一个接口:声明要添加的,并自实现的方法
2.提供该接口的实现类:类名需在要声明的 Repository 后添加 Impl, 并实现方法。
3.声明 Repository 接口,并继承 1> 声明的接口
4.使用
注意:默认情况下,Spring Data 会在 base-package 中查找"接口名 Impl" 作为实现类。
也可以通过 repository-impl-postfix 声明后缀。
2.为所有的 Repository 都添加自实现的方法