1. 简介:

上篇文章咱们对 Querydsl-JPA 对了介绍以及基本讲解,下来咱们开始介绍一些平常咱们经常用的多表联查用 Querydsl-JPA 是如何实现的。

2. 基础脚本:

2.1:用户信息表

  1. -- Create table
  2. create table USER_TMW
  3. (
  4. id VARCHAR2(32 CHAR) not null,
  5. name VARCHAR2(32 CHAR),
  6. age NUMBER(19,2),
  7. money NUMBER(19,2),
  8. begin_time VARCHAR2(32 CHAR),
  9. end_time VARCHAR2(32 CHAR),
  10. dept_id VARCHAR2(32 CHAR)
  11. )
  12. -- Create/Recreate primary, unique and foreign key constraints
  13. alter table USER_TMW
  14. add primary key (ID)
  15. using index
  16. tablespace UFGOV
  17. pctfree 10
  18. initrans 2
  19. maxtrans 255
  20. storage
  21. (
  22. initial 64K
  23. next 1M
  24. minextents 1
  25. maxextents unlimited
  26. );
  27. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  28. values ('8a84a8b36bbbc8e1016bbbd803c60016', '老王', 20.00, 2000.00, '1567579914276', '1567579904276', 'C8477CE676B143E983260B45D05C06B3');
  29. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  30. values ('0000000000000000000111', '小王', 30.00, 1500.00, '1567579924276', '1567579904276', 'C8477CE676B143E983260B45D05C06B3');
  31. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  32. values ('0000000000000000000001', '王五', 18.00, 1800.00, '1567579934276', '1567579904276', '8a90959d6b88ce95016b8c547cfb03e7');
  33. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  34. values ('0000000000000000000011', '小刚', 25.00, 1000.00, '1567579944276', '1567579904276', '8a90959d6b88ce95016b8c547cfb03e7');
  35. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  36. values ('0000000000000000011111', '张三', 30.00, 2000.00, '1567579954276', '1567579904276', '8a90959d6b92c60e016b937f0d550080');
  37. insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
  38. values ('0000000000000000000021', '李四', 30.00, 3000.00, '1567579964276', '1567579904276', '8a90959d6b92c60e016b937f0d550080');

2.2: 部门信息表

  1. -- Create table
  2. create table DEPT_TMW
  3. (
  4. id VARCHAR2(32 CHAR) not null,
  5. dept_name VARCHAR2(32 CHAR),
  6. dept_no VARCHAR2(32 CHAR),
  7. create_time VARCHAR2(32 CHAR),
  8. p_dept_id VARCHAR2(32 CHAR)
  9. )
  10. -- Create/Recreate primary, unique and foreign key constraints
  11. alter table DEPT_TMW
  12. add primary key (ID)
  13. using index
  14. tablespace UFGOV
  15. pctfree 10
  16. initrans 2
  17. maxtrans 255
  18. storage
  19. (
  20. initial 64K
  21. next 1M
  22. minextents 1
  23. maxextents unlimited
  24. );
  25. insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
  26. values ('C8477CE676B143E983260B45D05C06B3', '研发部门', 'N001', '1567579904276', null);
  27. insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
  28. values ('8a90959d6b88ce95016b8c547cfb03e7', '测试部门', 'N002', '1567579804276', 'C8477CE676B143E983260B45D05C06B3');
  29. insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
  30. values ('8a90959d6b92c60e016b937f0d550080', '运维部门', 'N003', '1567579704276', '8a90959d6b88ce95016b8c547cfb03e7');

3.Querydsl-JPA 多表操作:

3.1:新增用户和部门的实体类

  1. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import org.hibernate.annotations.GenericGenerator;
  6. import javax.persistence.*;
  7. /**
  8. * @ProjectName: queryDsl
  9. * @Package: com.springboot.demo.bean
  10. * @ClassName: Dept
  11. * @Author: tianmengwei
  12. * @Description:
  13. * @Date: 2019/9/4 15:10
  14. */
  15. @AllArgsConstructor
  16. @NoArgsConstructor
  17. @Data
  18. @Entity
  19. @Table(name = "dept_tmw")
  20. @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
  21. public class Dept {
  22. @Id
  23. @GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-uuid")
  24. @GenericGenerator(name = "custom-uuid", strategy = "com.springboot.demo.bean.CustomUUIDGenerator")
  25. @Column(name = "id", nullable = false, length = 32)
  26. private String id;
  27. @Column(name = "dept_name")
  28. private String deptName;
  29. @Column(name = "dept_no")
  30. private String deptNo;
  31. @Column(name = "create_time")
  32. private String createTime;
  33. @Column(name = "p_dept_id")
  34. private String pDeptId;
  35. }
  36. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  37. import lombok.AllArgsConstructor;
  38. import lombok.Data;
  39. import lombok.NoArgsConstructor;
  40. import org.hibernate.annotations.GenericGenerator;
  41. import javax.persistence.*;
  42. import java.math.BigDecimal;
  43. /**
  44. * @ProjectName: queryDsl
  45. * @Package: com.springboot.demo
  46. * @ClassName: User
  47. * @Author: tianmengwei
  48. * @Description:
  49. * @Date: 2019/8/19 19:35
  50. */
  51. @AllArgsConstructor
  52. @NoArgsConstructor
  53. @Data
  54. @Entity
  55. @Table(name = "user_tmw")
  56. @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
  57. public class User {
  58. @Id
  59. @GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-uuid")
  60. @GenericGenerator(name = "custom-uuid", strategy = "com.springboot.demo.bean.CustomUUIDGenerator")
  61. @Column(name = "id", nullable = false, length = 32)
  62. private String id;
  63. @Column(name = "name", length = 10)
  64. private String name;
  65. @Column(name = "age")
  66. private Integer age;
  67. @Column(name = "money")
  68. private BigDecimal money;
  69. @Column(name = "begin_time")
  70. private String beginTime;
  71. @Column(name = "end_time")
  72. private String endTime;
  73. @Column(name = "dept_id")
  74. private String deptId;
  75. }

3.2: 多表关联查询 结果多字段拼接显示处理(concat())

  1. public void getDeptUserListByLeftJoin() {
  2. QDept qDept = QDept.dept;
  3. QUser qUser = QUser.user;
  4. List<Tuple> tupleList = jpaQueryFactory.select(qDept.deptNo.concat(":").concat(qDept.deptName).concat(":").concat(qUser.name),
  5. qUser.age, qUser.money).from(qDept).leftJoin(qUser).on(qDept.id.eq(qUser.deptId))
  6. .orderBy(qUser.age.desc()).fetch();
  7. List<Map<String, Object>> resultList = tupleList.stream().map(x -> {
  8. Map<String, Object> resultMap = new HashMap<>();
  9. resultMap.put("nameDept", x.get(0, Dept.class));
  10. resultMap.put("nameUser", x.get(0, User.class));
  11. resultMap.put("nameDeptUser", x.get(qDept.deptNo.concat(":").concat(qDept.deptName).concat(":").concat(qUser.name)));
  12. resultMap.put("age", x.get(qUser.age));
  13. resultMap.put("money", x.get(qUser.money));
  14. return resultMap;
  15. }).collect(Collectors.toList());
  16. String userQueryResultsStr = JSON.toJSONString(resultList);
  17. System.out.println("getDeptUserListByLeftJoin的结果集:" + userQueryResultsStr);
  18. }

此处,我们在获取返回结果根据 jpaFactory 关联查询返回的类型是 Tuple,Tuple 提供了以下两种方式, 可以根据下标和指定某个属性去获取:

JPA Querydsl - 图1

在根据下标获取的方法第二个形参是指定一个 Class,在此处测试写关联查询的任意一个实体类都可以。

JPA Querydsl - 图2

3.3: 关联查询结果 case when 在 querydsl 中的使用

此处 querydsl 提供了 CaseBuilder 类,我们可以使用该类对字段的值做处理转换,当然我们呢也可以在用 stream 流的时候也进行处理;

  1. public void getDeptUserListByJoin() {
  2. QDept qDept = QDept.dept;
  3. QUser qUser = QUser.user;
  4. StringExpression otherwise = new CaseBuilder().when(qUser.age.gt(18)).then("成年人").when(qUser.age.lt(18)).then("青少年")
  5. .otherwise("快成年了");
  6. List<Tuple> tupleList = jpaQueryFactory.select(qUser.name,
  7. otherwise, qUser.age, qUser.money).from(qDept).join(qUser).on(qDept.id.eq(qUser.deptId))
  8. .orderBy(qUser.age.desc()).fetch();
  9. List<Map<String, Object>> resultList = tupleList.stream().map(x -> {
  10. Map<String, Object> resultMap = new HashMap<>();
  11. resultMap.put("name", x.get(qUser.name));
  12. resultMap.put("age", x.get(1, User.class));
  13. return resultMap;
  14. }).collect(Collectors.toList());
  15. String userQueryResultsStr = JSON.toJSONString(resultList);
  16. System.out.println("getDeptUserListByJoin的结果集:" + userQueryResultsStr);
  17. }

3.4:Querydsl 子查询的使用:

此处 Querydsl 提供了 JPAExpressions 类,我们使用该类做子查询处理。

  1. public void getMaxMoneyUserInfo() {
  2. QUser qUser = QUser.user;
  3. List<User> userList = jpaQueryFactory.selectFrom(qUser)
  4. .where(qUser.money.eq(JPAExpressions.select(qUser.money.max()).from(qUser))).fetch();
  5. String userQueryResultsStr = JSON.toJSONString(userList);
  6. System.out.println("getMaxMoneyUserInfo的结果集:" + userQueryResultsStr);
  7. }

3.5:Querydsl 多表关联查询返回结果处理:

在上面的多表关联查询我们在 select() 的填充要查询的列名,jpaQueryFactory 处理返回的类型是 Tuple,我们通过流处理在根据需要查询的字段名或者下标获取相应的数据再填充到集合里,有些麻烦了;
此处我们可以通过使用 querydsl 提供的 Projections 类处理,代码如下: 定义一个 vo 类,属性与查询的字段名称一致(此处可以采用别名)即可自动装箱到对象里面。

  1. public void getDeptListResultDeal() {
  2. QDept qDept = QDept.dept;
  3. QUser qUser = QUser.user;
  4. List<UserVo> resultList = jpaQueryFactory.select(
  5. Projections.bean(UserVo.class,
  6. qDept.deptNo.concat(":").concat(qDept.deptName).concat(":").concat(qUser.name).as("deptUserName"),
  7. qUser.age, qUser.money)).from(qDept).leftJoin(qUser).on(qDept.id.eq(qUser.deptId))
  8. .orderBy(qUser.age.desc()).fetch();
  9. String userQueryResultsStr = JSON.toJSONString(resultList);
  10. System.out.println("getDeptListResultDeal的结果集:" + userQueryResultsStr);
  11. }

3.6:Querydsl 自查询的使用:

此处我们在处理自查询类似写 sql 一样,别名肯定不一样,所以我们在通过 querydsl 的时候创建的创建两个 Q 版的实体类对象,Q 版的实体类提供了有参构造,可以指定别名。

  1. public void getDeptParentInfo() {
  2. //select t.dept_name,t1.* from dept_tmw t right join dept_tmw t1 on t.id = t1.p_dept_id
  3. QDept dept1 = new QDept("dept1");
  4. QDept dept2 = new QDept("dept2");
  5. StringExpression otherwise = new CaseBuilder().when(dept1.deptName.isNull().or(dept1.deptName.isEmpty()))
  6. .then("总部")
  7. .otherwise(dept1.deptName);
  8. List<Tuple> fetch = jpaQueryFactory.select(otherwise.concat(":").concat(dept2.deptName), dept2.deptNo, dept2.createTime)
  9. .from(dept1).rightJoin(dept2).on(dept1.id.eq(dept2.pDeptId)).orderBy(dept2.deptNo.desc()).fetch();
  10. List<Map<String, Object>> collect = fetch.stream().map(x -> {
  11. Map<String, Object> resultMap = new HashMap<>();
  12. resultMap.put("name", x.get(0, Dept.class));
  13. resultMap.put("deptNo", x.get(dept2.deptNo));
  14. resultMap.put("createTime", x.get(dept2.createTime));
  15. return resultMap;
  16. }).collect(Collectors.toList());
  17. String userQueryResultsStr = JSON.toJSONString(collect);
  18. System.out.println("getDeptParentInfo的结果集:" + userQueryResultsStr);
  19. }

3.7:Querydsl 查询时间区间范围内的用户:

此处时间传递的值是个时间戳字符串,范围取值把字符串转换为了数值进行的区间查询。

  1. public void getUserListByBetweenCreateTime() {
  2. QUser qUser = QUser.user;
  3. List<User> fetch = jpaQueryFactory.selectFrom(qUser).where(qUser.beginTime.between("1567579924276", "1567579954276")).fetch();
  4. String userQueryResultsStr = JSON.toJSONString(fetch);
  5. System.out.println("getUserListByBetweenCreateTime的结果集:" + userQueryResultsStr);
  6. }

4. Querydsl 与 spring web 的整合:

此处 spring-data 提供了注解 @QuerydslPredicate 可以将 http 请求的参数转换为 Predicate;

以下代码如下链接通过访问:
http://localhost:8080/user/seach?age=30&money=3000
查询结果就是年龄为 30 并且 money 为 3000 的用户,但具体其他的模糊查询,区间查询还未知。

  1. import com.querydsl.core.types.Predicate;
  2. import com.springboot.demo.bean.User;
  3. import com.springboot.demo.dao.UserDao;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.data.querydsl.binding.QuerydslPredicate;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestMethod;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import java.util.List;
  11. @RestController
  12. @RequestMapping(value = "/user")
  13. public class UserController {
  14. @Autowired
  15. private UserDao userDao;
  16. @RequestMapping(value = "/seach", method = RequestMethod.GET)
  17. @ResponseBody
  18. //http://localhost:8080/ar/user/seach?age=30&money=3000
  19. public Iterable<User> getUsers(
  20. @QuerydslPredicate(root = User.class) Predicate predicate) {
  21. Iterable<User> list = userDao.findAll(predicate);
  22. return list;
  23. }
  24. }
  25. import com.springboot.demo.bean.User;
  26. import org.springframework.data.jpa.repository.JpaRepository;
  27. import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
  28. import org.springframework.data.querydsl.QuerydslPredicateExecutor;
  29. public interface UserDao extends JpaRepository<User, String>, JpaSpecificationExecutor<User>, QuerydslPredicateExecutor<User> {
  30. }

来源 | https://juejin.im/post/6844903935828836360
作者 | tianmengwei