Repository接口及查询接口规范
Repository接口
- 是什么
Repository
是一个空接口,即:是一个标记接口,表示任何继承它的接口都是仓库接口类- 若我们继承了
Repository
,则该接口会被Ioc容器表示为一个Repository Bean,放入到IOC容器中,进而可以在该接口中定义满足一定规范的方法 - 实际上也可以通过
@RepositoryDefinition(domainClass = Person.class, idClass = Long.class)
来替代继承Repository
接口
- 子接口
CrudRepository
:继承Repository
,实现一组CURD相关的方法PagingAndSortingRespository
:继承CrudRepository
,实现了一组分页排序相关的方法JpaRepository
:继承PagingAndSortingRespository
,实现了一组JPA规范相关的方法- 自定义的
XxxRepository
:需要继承JpaRepository
,这样该接口就具备了通用的数据访问控制层的能力 JpaSpecificationExecutor
:不属于Repository
体系,实现一组JPA Criteria
查询相关的方法
Repository 查询方法定义规范
1. 查询方法
-
方法必须以
find|read|get
开头 -
涉及查询条件时,条件的实行用条件关键字连接,
- 注意:条件属性需要首字母大写
-
支持属性的级联查询
-
若当前类有符合条件的属性,会优先使用属性,而不是使用级联属性
-
若要使用级联属性,则属性之间用 _ 连接。而为了避免歧义,推荐使用 _ 分隔的写法。如:
// Address类 @Entity @Table(name = "t_address") public class Address implements Serializable { private static final long serialVersionUID = -682608034116202529L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String province; private String city; @ManyToOne @JoinColumn(name = "person_id") private Person person; } // Person类 @Entity @Table(name = "t_person") public class Person implements Serializable { private static final long serialVersionUID = -5964437048712784999L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Integer age; private String gender; private String email; @Column(name = "address_id") // 捣乱的字段 private Long addressId; @OneToMany(mappedBy = "person") private Set<Address> address = new HashSet<>(); } // PersonDao中的getByAddress_Id必须使用 _ 分隔,而不能直接写 getByAddressId public interface PersonDao extends Repository<Person, Long> { List<Person> getByNameLike(String name); Person getByAddress_Id(Long id); }
-
缺点:
- 方法名很长
- 不能用于子查询
-
2. 注解
-
@Query
-
可以执行自定义的JPQL语句以更灵活的查询
// 查询id最大的那个person @Query("from Person p1 where p1.id = (select max(p2.id) from Person p2)") Person getPersonByMaxId();
-
注解传递参数
-
使用占位符:参数顺序必须和JPQL中的顺序一致
@Query("from Person p where p.name = ?1 and p.gender = ?2") Person query_getPersonByNameAndGender(String name, String gender);
-
使用命名参数:这种写法可以随便设置参数顺序
@Query("from Person p where p.name = :name and p.age = :age") Person query_getPersonByNameAndAge(@Param("age") Integer age, @Param("name") String name);
- @Param:
org.springframework.data.repository.query.Param;
- @Param:
-
-
spring data允许在占位符上添加%(适用于模糊查询like:
%占位符%
) -
@Query支持原生sql查询
@Query(value = "select count(1) from t_person", nativeQuery = true) Long getTotal();
-
-
@Modifying:可以使用该注解来实现通过JPQL修改和删除(JPQL不支持添加)
@Modifying @Query(value = "update Person p set p.name = ?1, p.age = ?2 where p.id = ?3") void updatePerson(String name, Integer age, Long id);
- 注意:更新和删除操作需要事务支持
- 在@Query中编写JPQL语句,但是必须添加@Modifying进行修饰来告诉JPA这是修改的操作
- 为什么@Query可以执行呢?
- 所有的
Repository
方法都有一个事务,但是却是只读事务
- 所有的
3. CRUDRepository
- 这个接口提供了基本增删改查方法
4. PagingAndSortingRepository
-
该接口提供了排序和分页的方法:方法的参数Pagable里面包含的有Sort
-
测试
-
测试分页:pageNum(页码)从0开始计数
@Test public void test08() { Pageable pageable = PageRequest.of(0, 10); Page<Person> personPage = personDao.findAll(pageable); System.out.println("总记录条数:" + personPage.getTotalElements()); System.out.println("总页数:" + personPage.getTotalPages()); System.out.println("当前页:" + personPage.getNumber()); System.out.println("当前页内容(数据):" + personPage.getContent()); System.out.println("当前页记录数:" + personPage.getNumberOfElements()); }
-
测试排序
@Test public void test09() { Sort sort = Sort.by(Sort.Direction.DESC, "id"); Iterable<Person> persons = personDao.findAll(sort); persons.forEach(System.out::println); }
-
测试分页排序
@Test public void test10() { // Pageable pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "age", "id"); // Sort sort = Sort.by(Sort.Direction.DESC, "age", "id"); Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "age"); Sort.Order order2 = new Sort.Order(Sort.Direction.ASC, "name"); Sort sort = Sort.by(order1, order2); Pageable pageable = PageRequest.of(0, 10, sort); Page<Person> personPage = personDao.findAll(pageable); personPage.forEach(System.out::println); }
-
5. JpaRepository
-
JpaRepository
中定义了一些新的方法,最主要是批量操作的方法 -
方法
2. <S extends T> List<S> saveAll(Iterable<S> var1):批量保存-
void flush():刷新。同步Jpa缓存和数据库
-
<S extends T> S saveAndFlush(S var1):相当于JPA的merge方法
-
void deleteInBatch(Iterable<T> var1):批量删除
-
void deleteAllInBatch():批量删除所有
-
6. JpaSpecificationExecutor
-
通过查询条件查询
-
方法
-
Optional<T> findOne(@Nullable Specification<T> spec)
:根据条件查询 -
findAll
:查询所有-
List<T> findAll(@Nullable Specification<T> spec)
:根据条件查询所有 -
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable)
:根据条件分页[排序] -
List<T> findAll(@Nullable Specification<T> spec, Sort sort)
:根据条件排序
-
-
long count(@Nullable Specification<T> spec)
:查询数量
-
-
Specification
接口:Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
- 该接口通常使用匿名内部类
- Predicate 代表查询条件
- Root 代表查询的实体类对象
- CriteriaQuery 可以从中查询到 root 对象,还可以用来添加查询条件,还可以结合EntityManager获取最终查询的
TypeQuery
对象 - CriteriaBuilder 用于创建Criteria相关对象的工厂,当然可以从中获取到 Predicate 对象
本节代码:点击此处