Repository接口及查询接口规范

Repository接口

  1. 是什么
    1. Repository是一个空接口,即:是一个标记接口,表示任何继承它的接口都是仓库接口类
    2. 若我们继承了Repository,则该接口会被Ioc容器表示为一个Repository Bean,放入到IOC容器中,进而可以在该接口中定义满足一定规范的方法
    3. 实际上也可以通过@RepositoryDefinition(domainClass = Person.class, idClass = Long.class)来替代继承Repository接口
  2. 子接口
    1. CrudRepository:继承Repository,实现一组CURD相关的方法
    2. PagingAndSortingRespository:继承CrudRepository,实现了一组分页排序相关的方法
    3. JpaRepository:继承PagingAndSortingRespository,实现了一组JPA规范相关的方法
    4. 自定义的XxxRepository:需要继承JpaRepository,这样该接口就具备了通用的数据访问控制层的能力
    5. JpaSpecificationExecutor:不属于Repository体系,实现一组 JPA Criteria 查询相关的方法

Repository 查询方法定义规范

1. 查询方法

  1. 方法必须以find|read|get开头

  2. 涉及查询条件时,条件的实行用条件关键字连接,

    • 注意:条件属性需要首字母大写
  3. 支持属性的级联查询

    1. 若当前类有符合条件的属性,会优先使用属性,而不是使用级联属性

    2. 若要使用级联属性,则属性之间用 _ 连接。而为了避免歧义,推荐使用 _ 分隔的写法。如:

      // 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);
      }
      
    3. 缺点:

      • 方法名很长
      • 不能用于子查询

2. 注解

  1. @Query

    1. 可以执行自定义的JPQL语句以更灵活的查询

      // 查询id最大的那个person
      @Query("from Person p1 where p1.id = (select max(p2.id) from Person p2)")
      Person getPersonByMaxId();
      
    2. 注解传递参数

      1. 使用占位符:参数顺序必须和JPQL中的顺序一致

        @Query("from Person p where p.name = ?1 and p.gender = ?2")
        Person query_getPersonByNameAndGender(String name, String gender);
        
      2. 使用命名参数:这种写法可以随便设置参数顺序

        @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;
    3. spring data允许在占位符上添加%(适用于模糊查询like:%占位符%

    4. @Query支持原生sql查询

      @Query(value = "select count(1) from t_person", nativeQuery = true)
      Long getTotal();
      
  2. @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

  • 测试

    1. 测试分页: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());
      }
      
    2. 测试排序

      @Test
      public void test09() {
          Sort sort = Sort.by(Sort.Direction.DESC, "id");
          Iterable<Person> persons = personDao.findAll(sort);
          persons.forEach(System.out::println);
      }
      
    3. 测试分页排序

      @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):批量保存

    1. void flush():刷新。同步Jpa缓存和数据库

    2. <S extends T> S saveAndFlush(S var1):相当于JPA的merge方法

    3. void deleteInBatch(Iterable<T> var1):批量删除

    4. void deleteAllInBatch():批量删除所有

6. JpaSpecificationExecutor

  • 通过查询条件查询

  • 方法

    1. Optional<T> findOne(@Nullable Specification<T> spec):根据条件查询

    2. 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):根据条件排序

    3. long count(@Nullable Specification<T> spec):查询数量

  • Specification接口:

    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
    
    1. 该接口通常使用匿名内部类
    2. Predicate 代表查询条件
    3. Root 代表查询的实体类对象
    4. CriteriaQuery 可以从中查询到 root 对象,还可以用来添加查询条件,还可以结合EntityManager获取最终查询的TypeQuery对象
    5. CriteriaBuilder 用于创建Criteria相关对象的工厂,当然可以从中获取到 Predicate 对象

本节代码:点击此处

posted @ 2019-12-11 14:48  _ann  阅读(2987)  评论(0编辑  收藏  举报