3 查询方法

3.1 查询方法配置

3.2 方法查询策略设置

@EnableJpaRepositories

3.5 查询策略的属性表达式

对于以下两个实体类, 在 JPA 可以以遍历嵌套属性定义约束创建查询,其创建及查找过程:

 具体实现参见:PartTree 及 PartTreeJpaQuery

1 @Entity
2 @Data
3 public class Person {
4     @Id
5     @GeneratedValue
6     private Long id;
7 
8     private Address address;
9 }
Person
1 @Entity
2 @Data
3 public class Address {
4     private Long id;
5 
6     private String zipCode;
7 
8 }
Address
1 public interface PersonRepository extends JpaRepository<Person, Long> {
2 
3     Person findByAddress_ZipCode(String zipCode);
4 }
PersonRepository

 3.6 查询结果的处理

类定义 

 1 @Entity
 2 @Data
 3 @Builder
 4 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
 5 public class Person {
 6     @Id
 7     @GeneratedValue(strategy = GenerationType.IDENTITY)
 8     private Long id;
 9     @EqualsAndHashCode.Include
10     private String firstName;
11     @EqualsAndHashCode.Include
12     private String lastName;
13     @OneToOne(targetEntity = Address.class, mappedBy = "person", cascade = CascadeType.ALL)
14     private Address address;
15 
16 
17     @Entity
18     @Data
19     @Builder
20     @EqualsAndHashCode(onlyExplicitlyIncluded = true)
21     static class Address {
22         @Id
23         @GeneratedValue(strategy = GenerationType.IDENTITY)
24         private Long id;
25         @EqualsAndHashCode.Include
26         private String zipCode;
27         @EqualsAndHashCode.Include
28         private String city;
29 
30         @OneToOne(targetEntity = Person.class)
31         @ToString.Exclude
32         private Person person;
33 
34     }
35 
36 
37 }

 

3.6.1 Pageable 和 Slice

1     Page<Person> findByLastName(String lastName, Pageable pageable);
2     Slice<Person> findByLastName(String lastName, Pageable pageable);
3     List<Person> findByLastName(String lastName, Sort sort);
4     List<Person> findByLastName(String lastName, Pageable pageable);

 

3.6.2 流式查询与异步查询

 1     // 需要调用 BaseStream#close() 关闭流
 2 
 3     Stream<Person> findByFirstName(String firstName);
 4 
 5     @Async
 6     Future<Person>  findByAddress(Person.Address address);
 7 
 8     @Async
 9     CompletableFuture<Person> findByLastName(String lastName);
10 
11     @Async
12     ListenableFuture<Person> findDistinctByAddress_ZipCode(String zipCode);

 

 3.6.3 Projections 扩展查询结果

3.6.3.1 结果映射

当只需要 firstName 和 lastName 的时候, 可以定义一个映射接口, 让 Spring Data 映射结果到这个接口上:

1 interface NamesOnly {
2 
3     String getFirstName();
4 
5     String getLastName();
6 
7 }

在 Repository 中使用该接口

1  List<NamesOnly> findByLastName(String lastName);

测试类: 见 3.6.3.2

3.6.3.2 @Value 支持

interface NamesOnly {
    @Value("#{target.firstName+'_'+target.lastName}")
    String summary();

    String getFirstName();

    String getLastName();

}

上边的接口等同于 NamesOnlyImpl , 可使用 NamesOnlyImpl  代替

 1 @Getter
 2 @Setter
 3 @AllArgsConstructor
 4 class NamesOnlyImpl implements NamesOnly {
 5     private final String firstName;
 6     private final String lastName;
 7 
 8     @Override
 9     public String fullName() {
10         return firstName.concat("_").concat(lastName);
11     }
12 }

测试类: 

 1 @SpringBootTest
 2 @RunWith(SpringRunner.class)
 3 @Transactional
 4 class PersonRepositoryTest {
 5     @Autowired
 6     private PersonRepository personRepository;
 7 
 8   //NamesOnly
 9     @Test
10     void testFindByLastName() {
11         List<Person> peoples = new ArrayList<>();
12 
13         final int count = 3;
14         //添加4个待测数据
15         // F0 L0 Z0 C0
16         // F1 L1 Z1 C1
17         // F2 L2 Z2 C2
18         // F3 L3 Z3 C3
19         for (int i = 0; i < count; i++) {
20             Person person = Person.builder()
21                     .firstName("F" + i)
22                     .lastName("L" + i)
23                     .address(Person.Address.builder()
24                             .zipCode("Z" + i)
25                             .city("C" + i).build()).build();
26 
27             Person.Address address = Person.Address.builder()
28                     .zipCode("Z" + i)
29                     .city("C" + i).person(person).build();
30 
31             person.setAddress(address);
32             peoples.add(person);
33         }
34         //设置第二个数据  F1 L1 Z1 C1 -> F1 L0 Z1 C1
35         peoples.get(1).setLastName("L0");
36         // 保存4条待测数据
37         personRepository.saveAll(peoples);
38 
39         // 查找出来已保存的数据
40         List<NamesOnly> namesOnlies = personRepository.findByLastName("L0");
41 
42         //筛选元数据中的两条目标数据   , 作与 持久化结果 的比较
43         // F0 L0 Z0 C0
44         // F1 L0 Z1 C1
45         List<Person> targetPeopleInList = peoples.stream().filter(person -> person.getFirstName().contentEquals("L0")).collect(Collectors.toList());
46 
47         // 断言符合 lastName = 'L0' 的持久化结果只有两条
48         Assert.assertEquals(2, namesOnlies.size());
49 
50         // 在目标数据表中删除对应的条目
51         targetPeopleInList.removeIf(
52                 person ->
53                         person.getFirstName().concat("_").concat(person.getLastName()).contentEquals(namesOnlies.get(0).summary()) ||
54                                 person.getFirstName().concat("_").concat(person.getLastName()).contentEquals(namesOnlies.get(1).summary())
55         );
56         // 目标数据此时应该为空
57         Assert.assertTrue(targetPeopleInList.isEmpty());
58 
59 
60     }
61 }

 

3.6.3.3 对 动态 Projections 的支持

1 public interface PersonRepository extends CrudRepository<Person, Long> {
2 
3     Collection<NamesOnly> findByLastName(String lastName);
4 
5 
6     <T> List<T> findByLastName(String lastName, Class<T> type);7 
8 }

 4 注解式查询

4.1 @Query

4.1.1 JPQL

    @Query(value = "select p from  Person p where p.firstName=?1")
    List<Person> findByFirstName(String firstName);  

4.1.2 JPQL+LIKE

    @Query(value = "select p from Person p where p.firstName like %?1")
    List<Person> findByFirstNameEndingWith(String ends);  

4.1.3 原生 SQL查询

    @Query(nativeQuery = true, value = "" +
            "select * " +
            "from person  inner join person$address p$a " +
            "on person.id = p$a.person_id where p$a.city=?1")
    Person findByAddress_City(String city);   

4.1.4 @Param 绑定参数

    @Query(value = "select p from  Person p where p.lastName=:lastName and p.firstName=:firstName")
    List<Person> findByFirstNameAndLastName(@Param("lastName") String lastName, @Param("firstName") String firstName);

4.1.5 JPQL 分页

    @Query(value = "select p from  Person p where p.firstName=?1")
    Page<Person> findByFirstName(String firstName, Pageable pageable);

 5 常用注解

5.1 JPA层次结构

 

5.2 常用注解

5.2.1 @Entity 持久化实体

5.2.2 @Table 表定义

5.2.3 @Id 主键

5.2.4 @IdClass 联合主键

在下例中, title 和 createUserId 是联合主键, 主要 BlogKey 需要实现 EqualsHashCode 方法

 1 @Entity
 2 @Table(name = "blog")
 3 @IdClass(BlogKey.class)
 4 public class Blog implements  Serializable{
 5 
 6     @Column(nullable = false)
 7     private String title;
 8     @Column(nullable = false)
 9     private Integer createUserId;
10 
11     @Lob
12     private String content;
13 
14 }
15 
16 @Data
17 class BlogKey implements Serializable {
18     private String title;
19     private Integer createUserId;
20 
21 }

其 Repository 

interface BlogRepository extends Repository<Blog, BlogKey> {

}

5.2.5 @GeneratedValue

生成策略(strategy)有4种

  1. GenerationType.TABLE:Hibernate 在数据库中新建一个表 hibernate_sequences, 用来表示下一个插入的主键数值 , 易于不同数据库间的移植;
  2. GenerationType.SEQUENCE:通过序列产生主键,不适用于 MySQL ,一般用于 Oracle 主键生成规则, 需要配合 @SequenceGenerator ;
  3. GenerationType.IDENTITY: 采用数据库ID自增长,一般用于 MySQL ;
  4. GenerationType.AUTO: 让 Hibernate 自行选择,默认选项;

5.2.6 @Basic 持久化字段

表示实体到数据库的映射,对于没有任何JPA注解的字段,默认即为 @Basic 

5.2.7 @Transient 非持久化字段

表示该字段并非一个到数据库表的映射

5.2.8 @Column 列属性

5.2.9 @Temporal Date 类型精度

5.2.10 @Enumerated 枚举类型

5.2.11 @Lob 大字段

5.2.12 @OrderBy 排序

5.2.13 附: 完整的类实例

 

 1 @Entity
 2 @Table(name = "blog")
 3 @IdClass(BlogKey.class)
 4 @Data
 5 @EqualsAndHashCode(onlyExplicitlyIncluded = true)
 6 public class Blog implements Serializable {
 7     @Id
 8     @Column(nullable = false)
 9     @EqualsAndHashCode.Include
10     private String title;
11     @Id
12     @Column(nullable = false)
13     @EqualsAndHashCode.Include
14     private Integer createUserId;
15 
16     @OrderBy("page_creatDate ASC ")
17     @OneToMany(targetEntity = Page.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "blog")
18     private List<Page> pages;
19 
20     @OneToOne(targetEntity = Owner.class, cascade = CascadeType.ALL, mappedBy = "blog")
21     private Owner owner;
22 }
23 
24 @Data
25 class BlogKey implements Serializable {
26     private String title;
27     private Integer createUserId;
28 
29 }
30 
31 @Entity
32 @Data
33 class Page {
34     @Id
35     @GeneratedValue(strategy = GenerationType.IDENTITY)
36     private Long id;
37     @Basic(fetch = FetchType.LAZY)
38     @Lob
39     private String content;
40     @Temporal(TemporalType.TIMESTAMP)
41     private Date createDate;
42 
43 
44     @ManyToOne(targetEntity = Blog.class, cascade = {CascadeType.REFRESH, CascadeType.MERGE})
45     private Blog blog;
46 
47 
48 }
49 
50 @Entity
51 @Data
52 class Owner {
53     @Id
54     @GeneratedValue(strategy = GenerationType.IDENTITY)
55     private Long id;
56     @Column(nullable = false, length = 30)
57     private String name;
58 
59     @OneToOne(targetEntity = Blog.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
60     private Blog blog;
61 
62     @Enumerated(EnumType.STRING)
63     private USERTYPE usertype;
64 }
65 
66 @AllArgsConstructor
67 enum USERTYPE {
68     /*
69         一般使用者
70      */
71     USER_ROMAL("USER_ROMAL"),
72     /*
73         管理员
74      */
75     USER_ADMIN("USER_ADMIN");
76     private String name;
77 }
78 
79 
80 interface BlogRepository extends Repository<Blog, BlogKey> {
81 
82 }

 

posted on 2019-10-30 12:07  四维胖次  阅读(288)  评论(0编辑  收藏  举报