基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:
Repository: 是 spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法
仅仅是一个标识,表明任何继承它的均为仓库接口类,方便Spring自动扫描识别,
@Indexed public interface Repository{ }
T :实体类名 ID : 主键类型
CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
@NoRepositoryBean
public interface CrudRepository extends Repository {
S save(S entity);
Iterable saveAll(Iterable entities);
Optional findById(ID id);
boolean existsById(ID id);
Iterable findAll();
Iterable findAllById(Iterable ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable extends T> entities);
void deleteAll();
}
PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
public interface PagingAndSortingRepositoryextends Serializable> extends CrudRepository<T, ID>
IterablefindAll(Sort sort); Page findAll(Pageable pageable); }
使用举例:
如果要在以20为一页的结果中,获取第2页结果,则如下使用:
Page users = repository.findAll(new PageRequest(1, 20));
JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法 。
(1)简单的条件查询的方法定义规范
- 简单条件查询:查询某一个实体或者集合
- 按照SpringData规范,查询方法于find|read|get开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:属性首字母需要大写。
- 支持属性的级联查询;若当前类有符合条件的属性, 则优先使用, 而不使用级联属性。 若需要使用级联属性, 则属性之间使用 _ 进行连接。
(2)支持的关键字
直接在接口中定义方法,如果符合规范,则不用写实现。目前支持的关键字写法如下:
(3)属性级联查询的案例
- 修改Person类,添加address属性,使Person和Address成多对一的关系,设置外键列名为address_id ,添加的代码如下图
- 在PersonDao接口中定义一个方法,代码如下:
// 级联查询,查询address的id等于条件值/ ListfindByAddressId(Integer addressId);
运行测试方法
/** 测试findByAddressId方法 */ @Test PersonDao personDao = ctx.getBean(PersonDao.class); // 查出地址id为1的person集合 Listlist = personDao.findByAddressId(1); for (Person person : list) { System.out.println(person.getName() + "---addressId=" + person.getAddress().getId()); } }
(4)查询方法解析流程
- 首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc
- 先判断 userDepUuid(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续往下走
- 从右往左截取第一个大写字母开头的字符串(此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复这一步,继续从右往左截取;最后假设 user 为查询实体的一个属性
- 接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据 "Doc.user.depUuid" 的取值进行查询;否则继续按照步骤3的规则从右往左截取,最终表示根据 "Doc.user.dep.uuid" 的值进行查询。
可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以 明确在级联的属性之间加上 "_" 以显式表达意图,比如 "findByUser_DepUuid()" 或者 "findByUserDep_uuid()"。
@Query注解
(1)使用Query结合jpql语句实现自定义查询
- 在PersonDao接口中声明方法,放上面加上Query注解,注解里面写jpql语句,代码如下:
// 自定义的查询,直接写jpql语句; 查询id 或者 名字 like?的person集合 @Query("from Person where id < ?1 or name like ?2") ListtestPerson(Integer id, String name); // 自定义查询之子查询,直接写jpql语句; 查询出id最大的person @Query("from Person where id = (select max(p.id) from Person as p)") Person testSubquery();
(2)索引参数和命名参数
一个特殊情况,那就是自定义的Query查询中jpql语句有like查询时,可以直接把%号写在参数的前后,这样传参数就不用把%号拼接进去了。使用案例如下,调用该方法时传递的参数直接传就ok。
(3)使用@Query来指定使用本地SQL查询
- dao层接口写法如下图所示
@Modifying注解和事务
1)@Modifying注解的使用
- dao层代码如下所示
//可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT //在 @Query 注解中编写 JPQL 语句, 但必须使用 @Modifying 进行修饰. 以通知 SpringData, 这是一个 UPDATE 或 DELETE 操作 //UPDATE 或 DELETE 操作需要使用事务, 此时需要定义 Service 层. 在 Service 层的方法上添加事务操作. //默认情况下, SpringData 的每个方法上有事务, 但都是一个只读事务. 他们不能完成修改操作! @Modifying @Query("UPDATE Person p SET p.name = :name WHERE p.id < :id") int updatePersonById(@Param("id")Integer id, @Param("name")String updateName);
- 方法返回值是int,表示影响的行数
- 在调用的地方必须加事务,没事务不执行
(2)事务
- Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。
- 对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional 声明
- 进行多个 Repository 操作时,也应该使它们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层,因此,需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。
本文参考博客:http://www.cnblogs.com/zeng1994/