SpringBoot12 QueryDSL02之利用QueryDSL實現多表關聯查詢
1 業務需求
有的系統業務邏輯比較復雜,存在著多表關聯查詢的的情況,查詢的內容不僅僅是單張表的的內容而是多張表的字段組合而成的,直接使用SplringDataJPA實現是比較復雜的,但是如果使用QueryDSL可以很方便的實現,而且利用QueryDSL實現的關聯查詢不僅可以只輸出單張表中滿足條件的內容還可以輸出多張表組合而成的字段
2 QueryDSL關聯查詢之單表數據輸出
2.1 創建兩個數據庫表
一個學生包和一個學校表,學校表和學生表時一對多的關系
/* Navicat MySQL Data Transfer Source Server : mysql5.4 Source Server Version : 50540 Source Host : localhost:3306 Source Database : springboot Target Server Type : MYSQL Target Server Version : 50540 File Encoding : 65001 Date: 2018-03-31 09:11:27View Code*/ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `querydsl_demo_school` -- ---------------------------- DROP TABLE IF EXISTS `querydsl_demo_school`; CREATE TABLE `querydsl_demo_school` ( `id` int(8) NOT NULL, `school` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of querydsl_demo_school -- ---------------------------- INSERT INTO `querydsl_demo_school` VALUES (‘1‘, ‘彌陀中學‘); INSERT INTO `querydsl_demo_school` VALUES (‘2‘, ‘大足中學‘); -- ---------------------------- -- Table structure for `querydsl_demo_student`-- ---------------------------- DROP TABLE IF EXISTS `querydsl_demo_student`; CREATE TABLE `querydsl_demo_student` ( `id` int(36) NOT NULL, `name` varchar(10) NOT NULL, `age` int(3) NOT NULL, `address` varchar(24) DEFAULT NULL, `school_id` int(8) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of querydsl_demo_student -- ---------------------------- INSERT INTO `querydsl_demo_student` VALUES (‘1‘, ‘warrior‘, ‘24‘, ‘重慶市大足區‘, ‘1‘); INSERT INTO `querydsl_demo_student` VALUES (‘2‘, ‘fury‘, ‘23‘, ‘渝足‘, ‘1‘); INSERT INTO `querydsl_demo_student` VALUES (‘3‘, ‘zeus‘, ‘32‘, ‘智鳳‘, ‘1‘); INSERT INTO `querydsl_demo_student` VALUES (‘4‘, ‘wys‘, ‘21‘, ‘廣工‘, ‘2‘); INSERT INTO `querydsl_demo_student` VALUES (‘5‘, ‘wym‘, ‘18‘, ‘松山湖‘, ‘2‘);
2.2 根據數據表創建實體類
package cn.test.demo.query_demo.model.javaModel; import lombok.Data; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; /** * @author 王楊帥 * @create 2018-03-30 21:16 * @desc 學校對應的實體類 **/ @Entity @Data @Table(name = "querydsl_demo_school") public class SchoolModel implements Serializable { @Id private Long id; private String school; }SchoolModel
package cn.test.demo.query_demo.model.javaModel; import lombok.Data; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import java.io.Serializable; /** * @author 王楊帥 * @create 2018-03-29 21:52 * @desc 學生實體類 **/ @Entity @Data @Table(name = "querydsl_demo_student") public class StudentModel implements Serializable { @Id private Long id; private String name; private Integer age; private String address; @Column(name = "school_id") private Long schoolId; }StudentModel.java
2.3 利用maven工具根據實體類創建查詢實體類
package cn.test.demo.query_demo.model.javaModel; import static com.querydsl.core.types.PathMetadataFactory.*; import com.querydsl.core.types.dsl.*; import com.querydsl.core.types.PathMetadata; import javax.annotation.Generated; import com.querydsl.core.types.Path; /** * QSchoolModel is a Querydsl query type for SchoolModel */ @Generated("com.querydsl.codegen.EntitySerializer") public class QSchoolModel extends EntityPathBase<SchoolModel> { private static final long serialVersionUID = 643543783L; public static final QSchoolModel schoolModel = new QSchoolModel("schoolModel"); public final NumberPath<Long> id = createNumber("id", Long.class); public final StringPath school = createString("school"); public QSchoolModel(String variable) { super(SchoolModel.class, forVariable(variable)); } public QSchoolModel(Path<? extends SchoolModel> path) { super(path.getType(), path.getMetadata()); } public QSchoolModel(PathMetadata metadata) { super(SchoolModel.class, metadata); } }QSchoolModel
package cn.test.demo.query_demo.model.javaModel; import static com.querydsl.core.types.PathMetadataFactory.*; import com.querydsl.core.types.dsl.*; import com.querydsl.core.types.PathMetadata; import javax.annotation.Generated; import com.querydsl.core.types.Path; /** * QStudentModel is a Querydsl query type for StudentModel */ @Generated("com.querydsl.codegen.EntitySerializer") public class QStudentModel extends EntityPathBase<StudentModel> { private static final long serialVersionUID = 751310108L; public static final QStudentModel studentModel = new QStudentModel("studentModel"); public final StringPath address = createString("address"); public final NumberPath<Integer> age = createNumber("age", Integer.class); public final NumberPath<Long> id = createNumber("id", Long.class); public final StringPath name = createString("name"); public final NumberPath<Long> schoolId = createNumber("schoolId", Long.class); public QStudentModel(String variable) { super(StudentModel.class, forVariable(variable)); } public QStudentModel(Path<? extends StudentModel> path) { super(path.getType(), path.getMetadata()); } public QStudentModel(PathMetadata metadata) { super(StudentModel.class, metadata); } }QStudentModel
2.4 創建持久層
package cn.test.demo.query_demo.dao; import cn.test.demo.query_demo.model.javaModel.SchoolModel; /** * @author 王楊帥 * @create 2018-03-30 21:19 * @desc **/ public interface SchoolJPA extends BaseJPA<SchoolModel> { }SchoolJPA.java
2.5 創建持久層
技巧01:select() 方法指明查詢結果只是來源於學生表,只不過這裏是利用學生表對應的查詢對象來表示的
坑01:雖然學校表和學生表是一對多的關系,但是select() 方法的參數就可以是學生表對應的查詢實體對象也可以是學校表對應的查詢實體對象,只不過select()中參數的類型決定了查詢結果來源於那張表
技巧02:from() 方法指明關聯查詢的表,一個是學生表,另一個是學校表,只不過這裏都是利用相關的查詢實體來表示的
技巧03:where() 方法中既可以指定表之間的關聯條件,又可以指定查詢結果的條件限制
技巧04:fetch() 方法是執行查詢操作並將查詢到的數據組裝成一個列表進行返回
@GetMapping(value = "/test") public List<StudentModel> test(@RequestParam("id") Long id) { QStudentModel _Q_studentModel = QStudentModel.studentModel; QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; return jpaQueryFactory.select(_Q_studentModel) .from(_Q_schoolModel, _Q_studentModel) .where(_Q_studentModel.schoolId.eq(_Q_schoolModel.id) .and(_Q_schoolModel.id.eq(id)) ) .fetch(); }
package cn.test.demo.query_demo.controller; import cn.test.demo.query_demo.model.javaModel.QSchoolModel; import cn.test.demo.query_demo.model.javaModel.QStudentModel; import cn.test.demo.query_demo.model.javaModel.SchoolModel; import cn.test.demo.query_demo.model.javaModel.StudentModel; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.persistence.EntityManager; import java.util.List; /** * @author 王楊帥 * @create 2018-03-30 21:20 * @desc **/ @RestController @Slf4j @RequestMapping(value = "/school") public class SchoolController { @Resource private EntityManager entityManager; private JPAQueryFactory jpaQueryFactory; @PostConstruct public void initFactory() { jpaQueryFactory = new JPAQueryFactory(entityManager); } @GetMapping(value = "/connect") public String connect() { String result = "測試school控制層"; log.info("測試信息為:{}", result); return result; } @GetMapping(value = "/findAll") public List<SchoolModel> findAll() { QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; List<SchoolModel> schoolModelList = jpaQueryFactory.selectFrom(_Q_schoolModel) .fetch(); log.info("查詢到的數據為:{}", schoolModelList); return schoolModelList; } @GetMapping(value = "/test") public List<StudentModel> test(@RequestParam("id") Long id) { QStudentModel _Q_studentModel = QStudentModel.studentModel; QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; return jpaQueryFactory.select(_Q_studentModel) .from(_Q_schoolModel, _Q_studentModel) .where(_Q_studentModel.schoolId.eq(_Q_schoolModel.id) .and(_Q_schoolModel.id.eq(id)) ) .fetch(); } }View Code
2.5 效果展示
這種關聯查詢只會將一個表中的數據查出,在本案例中只會將學生表中學校ID在學校表中出現的東西查詢出來(即:如果學生表中有一條記錄的學校ID不再學校表內,那麽這條記錄是不會被查詢出來的),而且最終的查詢結果沒有學校表的信息,只有學生表的信息
3 QueryDSL關聯查詢之多表數據輸出
3.1 創建數據庫表
3.2 根據數據庫表創建實體類
3.3 根據實體類創建查詢實體類
3.4 創建數據傳輸實體類
package cn.test.demo.query_demo.model.dtoModel; import lombok.Data; import java.io.Serializable; /** * @author 王楊帥 * @create 2018-03-31 9:32 * @desc 學生詳細信息實體 **/ @Data public class StudentDtoModel implements Serializable { private Long id; private String name; private Integer age; private String address; private Long schoolId; private String schoolname; }StudentDtoModel.java
3.5 創建持久層
3.6 創建控制層
技巧01:select() 方法中不再是簡單的查詢實體對象了,而是利用Porjections類提供的bean方法創建了一個新對象
技巧02:bean() 方法需要傳入多個參數,第一個參數是查詢結果封裝類,之後的參數是將查詢的字段和封裝類進行對應
技巧03:如果封裝類和查詢實體類的屬性名不一致時就需要別名,例如
學校表的對應的查詢實體的學校名稱字段是school而封裝類對應的學校名稱字段是schoolname,我們就需要利用下面的語句進行轉換
_Q_schoolModel.school.as("schoolname")
@GetMapping(value = "/test02") public List<StudentDtoModel> test02() { QStudentModel _Q_studnetModel = QStudentModel.studentModel; QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; return jpaQueryFactory.select(Projections.bean( StudentDtoModel.class, _Q_studnetModel.id, _Q_studnetModel.name, _Q_studnetModel.address, _Q_studnetModel.age, _Q_studnetModel.schoolId, _Q_schoolModel.school.as("schoolname") )).from(_Q_schoolModel, _Q_studnetModel) .where(_Q_schoolModel.id.eq(_Q_studnetModel.schoolId)) .fetch(); }}
package cn.test.demo.query_demo.controller; import cn.test.demo.query_demo.model.dtoModel.StudentDtoModel; import cn.test.demo.query_demo.model.javaModel.QSchoolModel; import cn.test.demo.query_demo.model.javaModel.QStudentModel; import cn.test.demo.query_demo.model.javaModel.SchoolModel; import cn.test.demo.query_demo.model.javaModel.StudentModel; import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.persistence.EntityManager; import java.util.List; /** * @author 王楊帥 * @create 2018-03-30 21:20 * @desc **/ @RestController @Slf4j @RequestMapping(value = "/school") public class SchoolController { @Resource private EntityManager entityManager; private JPAQueryFactory jpaQueryFactory; @PostConstruct public void initFactory() { jpaQueryFactory = new JPAQueryFactory(entityManager); } @GetMapping(value = "/connect") public String connect() { String result = "測試school控制層"; log.info("測試信息為:{}", result); return result; } @GetMapping(value = "/findAll") public List<SchoolModel> findAll() { QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; List<SchoolModel> schoolModelList = jpaQueryFactory.selectFrom(_Q_schoolModel) .fetch(); log.info("查詢到的數據為:{}", schoolModelList); return schoolModelList; } @GetMapping(value = "/test") public List<SchoolModel> test(@RequestParam("id") Long id) { QStudentModel _Q_studentModel = QStudentModel.studentModel; QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; return jpaQueryFactory.select(_Q_schoolModel) .from(_Q_schoolModel, _Q_studentModel) .where(_Q_studentModel.schoolId.eq(_Q_schoolModel.id) // .and(_Q_schoolModel.id.eq(id)) ) .fetch(); } @GetMapping(value = "/test02") public List<StudentDtoModel> test02() { QStudentModel _Q_studnetModel = QStudentModel.studentModel; QSchoolModel _Q_schoolModel = QSchoolModel.schoolModel; return jpaQueryFactory.select(Projections.bean( StudentDtoModel.class, _Q_studnetModel.id, _Q_studnetModel.name, _Q_studnetModel.address, _Q_studnetModel.age, _Q_studnetModel.schoolId, _Q_schoolModel.school.as("schoolname") )).from(_Q_schoolModel, _Q_studnetModel) .where(_Q_schoolModel.id.eq(_Q_studnetModel.schoolId)) .fetch(); }}View Code
4 QueryDSL參考文檔
點擊前往
SpringBoot12 QueryDSL02之利用QueryDSL實現多表關聯查詢