上篇文章我們對Querydsl-JPA對了介紹以及基本講解,下來我們開始介紹一些日常我們常常用的多表聯查用Querydsl-JPA是如何實現的。java
-- 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');
複製代碼
-- 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');
複製代碼
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;
}
複製代碼
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,在此處測試寫關聯查詢的任意一個實體類均可以。此處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);
}
複製代碼
此處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);
}
複製代碼
在上面的多表關聯查詢咱們在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);
}
複製代碼
此處咱們在處理自查詢相似寫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);
}
複製代碼
此處時間傳遞的值是個時間戳字符串,範圍取值把字符串轉換爲了數值進行的區間查詢。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);
}
複製代碼
此處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> {
}
複製代碼
經過對querydsl-jpa的學習瞭解,與spring-data-jpa比較,咱們在處理複雜查詢以及多表關聯的時候使用querydsl-jpa,可使代碼可讀性更高,而使用spring-data-jpa提供的dao層的繼承接口:JpaRepository以及JpaSpecificationExecutor讓咱們對單表的查詢操做更簡潔,因此在實際應用中咱們能夠結合的使用,取其優勢去其糟粕。測試