|
| 1 | +# JPA 连表查询和分页 |
| 2 | + |
| 3 | +对于连表查询,在 JPA 中还是非常常见的,由于 JPA 可以在 respository 层自定义 SQL 语句,所以通过自定义 SQL 语句的方式实现连表还是挺简单。这篇文章是在上一篇[入门 JPA](./springboot-jpa)的文章的基础上写的,不了解 JPA 的可以先看上一篇文章。 |
| 4 | + |
| 5 | +在[上一节](./springboot-jpa)的基础上我们新建了两个实体类,如下: |
| 6 | + |
| 7 | +## 相关实体类创建 |
| 8 | + |
| 9 | +`Company.java` |
| 10 | + |
| 11 | +```java |
| 12 | +@Entity |
| 13 | +@Data |
| 14 | +@NoArgsConstructor |
| 15 | +public class Company { |
| 16 | + @Id |
| 17 | + @GeneratedValue(strategy = GenerationType.IDENTITY) |
| 18 | + private Long id; |
| 19 | + @Column(unique = true) |
| 20 | + private String companyName; |
| 21 | + private String description; |
| 22 | + |
| 23 | + public Company(String name, String description) { |
| 24 | + this.companyName = name; |
| 25 | + this.description = description; |
| 26 | + } |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +`School.java` |
| 31 | + |
| 32 | +```java |
| 33 | +@Entity |
| 34 | +@Data |
| 35 | +@NoArgsConstructor |
| 36 | +@AllArgsConstructor |
| 37 | +public class School { |
| 38 | + @Id |
| 39 | + @GeneratedValue(strategy = GenerationType.IDENTITY) |
| 40 | + private Long id; |
| 41 | + @Column(unique = true) |
| 42 | + private String name; |
| 43 | + private String description; |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +## 自定义 SQL语句实现连表查询 |
| 48 | + |
| 49 | +假如我们当前要通过 person 表的 id 来查询 Person 的话,我们知道 Person 的信息一共分布在`Company`、`School`、`Person`这三张表中,所以,我们如果要把 Person 的信息都查询出来的话是需要进行连表查询的。 |
| 50 | + |
| 51 | +首先我们需要创建一个包含我们需要的 Person 信息的 DTO 对象,我们简单第将其命名为 `UserDTO`,用于保存和传输我们想要的信息。 |
| 52 | + |
| 53 | +```java |
| 54 | +@Data |
| 55 | +@NoArgsConstructor |
| 56 | +@Builder(toBuilder = true) |
| 57 | +@AllArgsConstructor |
| 58 | +public class UserDTO { |
| 59 | + private String name; |
| 60 | + private int age; |
| 61 | + private String companyName; |
| 62 | + private String schoolName; |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +下面我们就来写一个方法查询出 Person 的基本信息。 |
| 67 | + |
| 68 | +```java |
| 69 | + /** |
| 70 | + * 连表查询 |
| 71 | + */ |
| 72 | + @Query(value = "select new github.snailclimb.jpademo.model.dto.UserDTO(p.name,p.age,c.companyName,s.name) " + |
| 73 | + "from Person p left join Company c on p.companyId=c.id " + |
| 74 | + "left join School s on p.schoolId=s.id " + |
| 75 | + "where p.id=:personId") |
| 76 | + Optional<UserDTO> getUserInformation(@Param("personId") Long personId); |
| 77 | +``` |
| 78 | + |
| 79 | +可以看出上面的 sql 语句和我们平时写的没啥区别,差别比较大的就是里面有一个 new 对象的操作。 |
| 80 | + |
| 81 | +## 自定义 SQL 语句连表查询并实现分页操作 |
| 82 | + |
| 83 | +假如我们要查询当前所有的人员信息并实现分页的话,你可以按照下面这种方式来做。可以看到,为了实现分页,我们在`@Query`注解中还添加了 **countQuery** 属性。 |
| 84 | + |
| 85 | +```java |
| 86 | +@Query(value = "select new github.snailclimb.jpademo.model.dto.UserDTO(p.name,p.age,c.companyName,s.name) " + |
| 87 | + "from Person p left join Company c on p.companyId=c.id " + |
| 88 | + "left join School s on p.schoolId=s.id ", |
| 89 | + countQuery = "select count(p.id) " + |
| 90 | + "from Person p left join Company c on p.companyId=c.id " + |
| 91 | + "left join School s on p.schoolId=s.id ") |
| 92 | +Page<UserDTO> getUserInformationList(Pageable pageable); |
| 93 | +``` |
| 94 | + |
| 95 | +实际使用: |
| 96 | + |
| 97 | +```java |
| 98 | +//分页选项 |
| 99 | +PageRequest pageRequest = PageRequest.of(0, 3, Sort.Direction.DESC, "age"); |
| 100 | +Page<UserDTO> userInformationList = personRepository.getUserInformationList(pageRequest); |
| 101 | +//查询结果总数 |
| 102 | +System.out.println(userInformationList.getTotalElements());// 6 |
| 103 | +//按照当前分页大小,总页数 |
| 104 | +System.out.println(userInformationList.getTotalPages());// 2 |
| 105 | +System.out.println(userInformationList.getContent()); |
| 106 | +``` |
| 107 | + |
| 108 | +## 加餐:自定以SQL语句的其他用法 |
| 109 | + |
| 110 | +### IN 查询 |
| 111 | + |
| 112 | + 在 sql 语句中加入我们需要筛选出符合几个条件中的一个的情况下,可以使用 IN 查询,对应到 JPA 中也非常简单。比如下面的方法就实现了,根据名字过滤需要的人员信息。 |
| 113 | + |
| 114 | +```java |
| 115 | +@Query(value = "select new github.snailclimb.jpademo.model.dto.UserDTO(p.name,p.age,c.companyName,s.name) " + |
| 116 | + "from Person p left join Company c on p.companyId=c.id " + |
| 117 | + "left join School s on p.schoolId=s.id " + |
| 118 | + "where p.name IN :peopleList") |
| 119 | +List<UserDTO> filterUserInfo(List peopleList); |
| 120 | +``` |
| 121 | + |
| 122 | +实际使用: |
| 123 | + |
| 124 | +```java |
| 125 | +List<String> personList=new ArrayList<>(Arrays.asList("person1","person2")); |
| 126 | +List<UserDTO> userDTOS = personRepository.filterUserInfo(personList); |
| 127 | +``` |
| 128 | + |
| 129 | +### BETWEEN 查询 |
| 130 | + |
| 131 | +查询满足某个范围的值。比如下面的方法就实现查询满足某个年龄范围的人员的信息。 |
| 132 | + |
| 133 | +```java |
| 134 | + @Query(value = "select new github.snailclimb.jpademo.model.dto.UserDTO(p.name,p.age,c.companyName,s.name) " + |
| 135 | + "from Person p left join Company c on p.companyId=c.id " + |
| 136 | + "left join School s on p.schoolId=s.id " + |
| 137 | + "where p.age between :small and :big") |
| 138 | + List<UserDTO> filterUserInfoByAge(int small,int big); |
| 139 | +``` |
| 140 | + |
| 141 | +实际使用: |
| 142 | + |
| 143 | +```java |
| 144 | +List<UserDTO> userDTOS = personRepository.filterUserInfoByAge(19,20); |
| 145 | +``` |
| 146 | + |
0 commit comments