Querydsl-JPA學習(進階篇)

1.簡介:

上篇文章我們對Querydsl-JPA對了介紹以及基本講解,下來我們開始介紹一些日常我們常常用的多表聯查用Querydsl-JPA是如何實現的。java

2.基礎腳本:

2.1:用戶信息表

-- Create table
create table USER_TMW
(
  id         VARCHAR2(32 CHAR) not null,
  name       VARCHAR2(32 CHAR),
  age        NUMBER(19,2),
  money      NUMBER(19,2),
  begin_time VARCHAR2(32 CHAR),
  end_time   VARCHAR2(32 CHAR),
  dept_id    VARCHAR2(32 CHAR)
)
-- Create/Recreate primary, unique and foreign key constraints 
alter table USER_TMW
  add primary key (ID)
  using index 
  tablespace UFGOV
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
複製代碼
insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('8a84a8b36bbbc8e1016bbbd803c60016', '老王', 20.00, 2000.00, '1567579914276', '1567579904276', 'C8477CE676B143E983260B45D05C06B3');

insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('0000000000000000000111', '小王', 30.00, 1500.00, '1567579924276', '1567579904276', 'C8477CE676B143E983260B45D05C06B3');

insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('0000000000000000000001', '王五', 18.00, 1800.00, '1567579934276', '1567579904276', '8a90959d6b88ce95016b8c547cfb03e7');

insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('0000000000000000000011', '小剛', 25.00, 1000.00, '1567579944276', '1567579904276', '8a90959d6b88ce95016b8c547cfb03e7');

insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('0000000000000000011111', '張三', 30.00, 2000.00, '1567579954276', '1567579904276', '8a90959d6b92c60e016b937f0d550080');

insert into user_tmw (ID, NAME, AGE, MONEY, BEGIN_TIME, END_TIME, DEPT_ID)
values ('0000000000000000000021', '李四', 30.00, 3000.00, '1567579964276', '1567579904276', '8a90959d6b92c60e016b937f0d550080');
複製代碼

2.2:部門信息表

-- Create table
create table DEPT_TMW
(
  id          VARCHAR2(32 CHAR) not null,
  dept_name   VARCHAR2(32 CHAR),
  dept_no     VARCHAR2(32 CHAR),
  create_time VARCHAR2(32 CHAR),
  p_dept_id   VARCHAR2(32 CHAR)
)
-- Create/Recreate primary, unique and foreign key constraints 
alter table DEPT_TMW
  add primary key (ID)
  using index 
  tablespace UFGOV
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    next 1M
    minextents 1
    maxextents unlimited
  );
複製代碼
insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
values ('C8477CE676B143E983260B45D05C06B3', '研發部門', 'N001', '1567579904276', null);

insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
values ('8a90959d6b88ce95016b8c547cfb03e7', '測試部門', 'N002', '1567579804276', 'C8477CE676B143E983260B45D05C06B3');

insert into dept_tmw (ID, DEPT_NAME, DEPT_NO, CREATE_TIME, P_DEPT_ID)
values ('8a90959d6b92c60e016b937f0d550080', '運維部門', 'N003', '1567579704276', '8a90959d6b88ce95016b8c547cfb03e7');
複製代碼

3.Querydsl-JPA多表操做:

3.1:新增用戶和部門的實體類

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

/**
 * @ProjectName: queryDsl
 * @Package: com.springboot.demo.bean
 * @ClassName: Dept
 * @Author: tianmengwei
 * @Description:
 * @Date: 2019/9/4 15:10
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(name = "dept_tmw")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Dept {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-uuid")
    @GenericGenerator(name = "custom-uuid", strategy = "com.springboot.demo.bean.CustomUUIDGenerator")
    @Column(name = "id", nullable = false, length = 32)
    private String id;

    @Column(name = "dept_name")
    private String deptName;

    @Column(name = "dept_no")
    private String deptNo;

    @Column(name = "create_time")
    private String createTime;

    @Column(name = "p_dept_id")
    private String pDeptId;
}
複製代碼
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.math.BigDecimal;

/**
 * @ProjectName: queryDsl
 * @Package: com.springboot.demo
 * @ClassName: User
 * @Author: tianmengwei
 * @Description:
 * @Date: 2019/8/19 19:35
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(name = "user_tmw")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-uuid")
    @GenericGenerator(name = "custom-uuid", strategy = "com.springboot.demo.bean.CustomUUIDGenerator")
    @Column(name = "id", nullable = false, length = 32)
    private String id;

    @Column(name = "name", length = 10)
    private String name;

    @Column(name = "age")
    private Integer age;

    @Column(name = "money")
    private BigDecimal money;

    @Column(name = "begin_time")
    private String beginTime;

    @Column(name = "end_time")
    private String endTime;

    @Column(name = "dept_id")
    private String deptId;
}
複製代碼

3.2:多表關聯查詢 結果多字段拼接顯示處理(concat())

public void getDeptUserListByLeftJoin() {
    QDept qDept = QDept.dept;
    QUser qUser = QUser.user;
    List<Tuple> tupleList = jpaQueryFactory.select(qDept.deptNo.concat(":").concat(qDept.deptName).concat(":").concat(qUser.name),
            qUser.age, qUser.money).from(qDept).leftJoin(qUser).on(qDept.id.eq(qUser.deptId))
            .orderBy(qUser.age.desc()).fetch();
    List<Map<String, Object>> resultList = tupleList.stream().map(x -> {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("nameDept", x.get(0, Dept.class));
        resultMap.put("nameUser", x.get(0, User.class));
        resultMap.put("nameDeptUser", x.get(qDept.deptNo.concat(":").concat(qDept.deptName).concat(":").concat(qUser.name)));
        resultMap.put("age", x.get(qUser.age));
        resultMap.put("money", x.get(qUser.money));
        return resultMap;
    }).collect(Collectors.toList());
    String userQueryResultsStr = JSON.toJSONString(resultList);
    System.out.println("getDeptUserListByLeftJoin的結果集:" + userQueryResultsStr);
}
複製代碼

此處,咱們在獲取返回結果根據jpaFactory關聯查詢返回的類型是Tuple,Tuple提供瞭如下兩種方式,能夠根據下標和指定某個屬性去獲取: web

在根據下標獲取的方法第二個形參是指定一個Class,在此處測試寫關聯查詢的任意一個實體類均可以。

3.3:關聯查詢結果 case when在querydsl中的使用

此處querydsl提供了CaseBuilder類,咱們可使用該類對字段的值作處理轉換,固然咱們呢也能夠在用stream流的時候也進行處理;spring

public void getDeptUserListByJoin() {
    QDept qDept = QDept.dept;
    QUser qUser = QUser.user;
    StringExpression otherwise = new CaseBuilder().when(qUser.age.gt(18)).then("成年人").when(qUser.age.lt(18)).then("青少年")
            .otherwise("快成年了");

    List<Tuple> tupleList = jpaQueryFactory.select(qUser.name,
            otherwise, qUser.age, qUser.money).from(qDept).join(qUser).on(qDept.id.eq(qUser.deptId))
            .orderBy(qUser.age.desc()).fetch();
    List<Map<String, Object>> resultList = tupleList.stream().map(x -> {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("name", x.get(qUser.name));
        resultMap.put("age", x.get(1, User.class));
        return resultMap;
    }).collect(Collectors.toList());

    String userQueryResultsStr = JSON.toJSONString(resultList);
    System.out.println("getDeptUserListByJoin的結果集:" + userQueryResultsStr);
}
複製代碼

3.4:Querydsl子查詢的使用:

此處Querydsl提供了JPAExpressions類,咱們使用該類作子查詢處理。sql

public void getMaxMoneyUserInfo() {
QUser qUser = QUser.user;
List<User> userList = jpaQueryFactory.selectFrom(qUser)
        .where(qUser.money.eq(JPAExpressions.select(qUser.money.max()).from(qUser))).fetch();
String userQueryResultsStr = JSON.toJSONString(userList);
System.out.println("getMaxMoneyUserInfo的結果集:" + userQueryResultsStr);
}
複製代碼

3.5:Querydsl多表關聯查詢返回結果處理:

在上面的多表關聯查詢咱們在select()的填充要查詢的列名,jpaQueryFactory處理返回的類型是Tuple,咱們經過流處理在根據須要查詢的字段名或者下標獲取相應的數據再填充到集合裏,有些麻煩了;
此處咱們能夠經過使用querydsl提供的Projections類處理,代碼以下: 定義一個vo類,屬性與查詢的字段名稱一致(此處能夠採用別名)便可自動裝箱到對象裏面。springboot

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

複製代碼

3.6:Querydsl自查詢的使用:

此處咱們在處理自查詢相似寫sql同樣,別名確定不同,因此咱們在經過querydsl的時候建立的建立兩個Q版的實體類對象,Q版的實體類提供了有參構造,能夠指定別名。bash

public void getDeptParentInfo() {
    //select t.dept_name,t1.* from dept_tmw t right join dept_tmw t1 on t.id = t1.p_dept_id
    QDept dept1 = new QDept("dept1");
    QDept dept2 = new QDept("dept2");

    StringExpression otherwise = new CaseBuilder().when(dept1.deptName.isNull().or(dept1.deptName.isEmpty()))
            .then("總部")
            .otherwise(dept1.deptName);
    List<Tuple> fetch = jpaQueryFactory.select(otherwise.concat(":").concat(dept2.deptName), dept2.deptNo, dept2.createTime)
            .from(dept1).rightJoin(dept2).on(dept1.id.eq(dept2.pDeptId)).orderBy(dept2.deptNo.desc()).fetch();
    List<Map<String, Object>> collect = fetch.stream().map(x -> {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("name", x.get(0, Dept.class));
        resultMap.put("deptNo", x.get(dept2.deptNo));
        resultMap.put("createTime", x.get(dept2.createTime));
        return resultMap;
    }).collect(Collectors.toList());
    String userQueryResultsStr = JSON.toJSONString(collect);
    System.out.println("getDeptParentInfo的結果集:" + userQueryResultsStr);
}
複製代碼

3.7:Querydsl查詢時間區間範圍內的用戶:

此處時間傳遞的值是個時間戳字符串,範圍取值把字符串轉換爲了數值進行的區間查詢。app

public void getUserListByBetweenCreateTime() {
    QUser qUser = QUser.user;
    List<User> fetch = jpaQueryFactory.selectFrom(qUser).where(qUser.beginTime.between("1567579924276", "1567579954276")).fetch();
    String userQueryResultsStr = JSON.toJSONString(fetch);
    System.out.println("getUserListByBetweenCreateTime的結果集:" + userQueryResultsStr);
}
複製代碼

4. Querydsl與spring web的整合:

此處spring-data提供了註解@QuerydslPredicate能夠將http請求的參數轉換爲Predicate;運維

如下代碼以下連接經過訪問:
http://localhost:8080/user/seach?age=30&money=3000
查詢結果就是年齡爲30而且money爲3000的用戶,但具體其餘的模糊查詢,區間查詢還未知。學習

import com.querydsl.core.types.Predicate;
import com.springboot.demo.bean.User;
import com.springboot.demo.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserDao userDao;

    @RequestMapping(value = "/seach", method = RequestMethod.GET)
    @ResponseBody
    //http://localhost:8080/ar/user/seach?age=30&money=3000
    public Iterable<User> getUsers(
                 @QuerydslPredicate(root = User.class) Predicate predicate) {
        Iterable<User> list = userDao.findAll(predicate);
        return list;
    }
}
複製代碼
import com.springboot.demo.bean.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

public interface UserDao extends JpaRepository<User, String>, JpaSpecificationExecutor<User>, QuerydslPredicateExecutor<User> {
}
複製代碼

5. 總結:

經過對querydsl-jpa的學習瞭解,與spring-data-jpa比較,咱們在處理複雜查詢以及多表關聯的時候使用querydsl-jpa,可使代碼可讀性更高,而使用spring-data-jpa提供的dao層的繼承接口:JpaRepository以及JpaSpecificationExecutor讓咱們對單表的查詢操做更簡潔,因此在實際應用中咱們能夠結合的使用,取其優勢去其糟粕。測試

相關文章
相關標籤/搜索