Spring JPA整合QueryDSL的示例代碼
前言
Spring JPA是目前比較常用的ORM解決方案,但是其對于某些場景并不是特別的方便,例如查詢部分字段,聯(lián)表查詢,子查詢等。
而接下來我會介紹與JPA形成互補,同時也是與JPA兼容得很好的框架QueryDSL。
同時由于目前主流使用Spring Boot,所以本文也會基于Spring Boot來進(jìn)行演示
如果對于長文無感,但是又希望了解QueryDSL可以直接查看文章最后的總結(jié)
環(huán)境信息
以下為示例的關(guān)鍵環(huán)境信息
- JDK 1.8
- maven 3.6.1
- SpringBoot 2.2.0.RELEASE
- IntelliJ IDEA 2019.2.3
- lombok
- mysql-5.7
源碼地址
https://github.com/spring-based-solutions/querydsl-jpa-demo
項目整合
pom文件配置
QueryDSL本身定位就是對某些技術(shù)的補充或者說是完善,其提供了對JPA、JDBC、JDO等技術(shù)的支持。這里引入的是QueryDSL-JPA,需要注意一定要引入querydsl代碼生成器插件。
<properties>
<java.version>1.8</java.version>
<querydsl.version>4.2.1</querydsl.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--使用版本較老的mysql驅(qū)動包,用于連接mysql-5.7-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入querydsl-jpa依賴-->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--引入querydsl代碼生成器插件-->
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
</dependencies>
<executions>
<!--設(shè)置插件生效的maven生命周期-->
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<!--配置生成文件的目錄-->
<outputDirectory>src/generated-sources/java/</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
application配置文件
spring: datasource: ## 數(shù)據(jù)庫相關(guān)配置 url: jdbc:mysql://127.0.0.1:3306/example?useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver # 指定驅(qū)動類 jpa: hibernate: ddl-auto: update # 自動創(chuàng)建表以及更新表結(jié)構(gòu),生產(chǎn)環(huán)境慎用 show-sql: true # 打印執(zhí)行的SQL
配置類
由于QueryDSL不提供starter,所以需要自行準(zhǔn)備一個配置類,代碼如下所示
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* QueryDSL配置類
* @author Null
* @date 2019-10-24
*/
@Configuration
public class QuerydslConfig {
@Autowired
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory queryFactory(){
return new JPAQueryFactory(entityManager);
}
}
啟動類
啟動類很簡單,只需要使用@SpringBootApplication即可
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class QuerydslJpaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(QuerydslJpaDemoApplication.class, args);
}
}
實體類
主要有講師和課程,每個課程都有一個講師,每個講師有多個課程,即講師與課程的關(guān)系為一對多
課程
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* 課程,一個課程對應(yīng)一個講師
* @author Null
* @date 2019-10-24
*/
@Data
@Entity
public class Course {
/**
* 課程ID
*/
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
/**
* 課程名稱
*/
private String name;
/**
* 對應(yīng)講師的ID
*/
private Long lecturerId;
}
講師
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* 講師,一個講師有多個課程
* @author Null
* @date 2019-10-24
*/
@Data
@Entity
public class Lecturer {
/**
* 講師ID
*/
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
/**
* 講師名字
*/
private String name;
/**
* 性別,true(1)為男性,false(0)為女性
*/
private Boolean sex;
}
Repository接口
如果要使用QuerDSL需要Repository接口除了繼承JpaRepository接口(此接口為Spring-JPA提供的接口)外,還需要繼承QuerydslPredicateExecutor接口。關(guān)鍵示例如下:
課程Repository
import com.example.querydsl.jpa.entity.Course;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
/**
* 課程Repository
*
* @author Null
* @date 2019-10-24
*/
public interface CourseRepository extends
JpaRepository<Course, Integer>,
QuerydslPredicateExecutor<Course> {
}
講師Repository
import com.example.querydsl.jpa.entity.Lecturer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
/**
* 講師Repository
* @author Null
* @date 2019-10-24
*/
public interface LecturerRepository extends
JpaRepository<Lecturer,Integer>,
QuerydslPredicateExecutor<Lecturer> {
}
代碼生成
前面配置QueryDSL代碼生成器就是用于這一步,==每次實體類有變更最好重復(fù)執(zhí)行本步驟重新生成新的代碼==。由于個人習(xí)慣使用IDEA,所以以IDEA作為演示。
雙擊下圖內(nèi)容即可生成代碼了,

然后就會在src/generated-sources目錄可以看到生成的代碼,包名與實體包名一致,但是類名為Q開頭的文件

上一步的截圖我們可以看到其實生成的代碼被IDEA識別為普通文件了,所以我們需要標(biāo)記src/generated-sources/java目錄的用途,如下圖所示

標(biāo)記后,效果如下,可以看到代碼被正確識別了

到了這一步其實已經(jīng)完成整合了,下面就開始驗證是否正確整合以及展示QueryDSL的優(yōu)勢了
驗證整合與演示
下面我會使用單元測試來驗證QueryDSL是否正確整合以及演示一下QueryDSL的優(yōu)勢
單元測試類
這里主要是單元測試類的關(guān)鍵內(nèi)容,需要注意@BeforeEach是Junit5的注解,表示每個單元測試用例執(zhí)行前會執(zhí)行的方法其實對應(yīng)Junit4的@Before
/**
* @SpringBootTest 默認(rèn)不支持事務(wù)且自動回滾
* 使用@Transactional 開啟事務(wù),
* 使用@Rollback(false) 關(guān)閉自動回滾
* @author Null
* @date 2019-10-24
*/
@SpringBootTest
class QuerydslJpaDemoApplicationTests {
@Autowired
private CourseRepository courseRepository;
@Autowired
private LecturerRepository lecturerRepository;
@Autowired
private JPAQueryFactory queryFactory;
/**
* 初始化數(shù)據(jù)
*/
@BeforeEach
public void initData(){
// 清空數(shù)據(jù)表
courseRepository.deleteAll();
lecturerRepository.deleteAll();
// 初始化講師
Lecturer tom=new Lecturer();
tom.setName("Tom");
tom.setSex(true);
lecturerRepository.save(tom);
Lecturer marry=new Lecturer();
marry.setName("Marry");
marry.setSex(false);
lecturerRepository.save(marry);
// 初始化課程
Course chinese=new Course();
chinese.setName("Chinese");
chinese.setLecturerId(tom.getId());
courseRepository.save(chinese);
Course physics=new Course();
physics.setName("Physics");
physics.setLecturerId(tom.getId());
courseRepository.save(physics);
Course english=new Course();
english.setName("English");
english.setLecturerId(marry.getId());
courseRepository.save(english);
}
...省略各個用例
}
單表模糊查詢
/**
* 根據(jù)課程名稱模糊查詢課程
*/
@Test
public void testSelectCourseByNameLike() {
// 組裝查詢條件
QCourse qCourse = QCourse.course;
// %要自行組裝
BooleanExpression expression = qCourse.name.like("P%");
System.out.println(courseRepository.findAll(expression));
}
聯(lián)表查詢
/**
* 根據(jù)講師姓名查課程
*/
@Test
public void testSelectCourseByLecturerName(){
QCourse qCourse = QCourse.course;
QLecturer qLecturer = QLecturer.lecturer;
// 這里包含了組裝查詢條件和執(zhí)行查詢的邏輯,組裝好條件后記得執(zhí)行fetch()
List<Course> courses=queryFactory.select(qCourse)
.from(qCourse)
.leftJoin(qLecturer)
.on(qCourse.lecturerId.eq(qLecturer.id))
.where(qLecturer.name.eq("Tom"))
.fetch();
System.out.println(courses);
}
更新
/**
* 根據(jù)姓名更新講師性別<br/>
* 使用@Transactional開啟事務(wù)<br/>
* 使用@Rollback(false)關(guān)閉自動回滾<br/>
*/
@Test
@Transactional
@Rollback(false)
public void testUpdateLecturerSexByName(){
QLecturer qLecturer = QLecturer.lecturer;
// 更新Tom的性別為女性,返回的是影響記錄條數(shù)
long num=queryFactory.update(qLecturer)
.set(qLecturer.sex,false)
.where(qLecturer.name.eq("Tom"))
.execute();
// 這里輸出被更新的記錄數(shù)
System.out.println(num);
}
刪除
/**
* 根據(jù)根據(jù)性別刪除講師
*/
@Test
@Transactional
@Rollback(false)
public void testDeleteLecturerBySex(){
QLecturer qLecturer = QLecturer.lecturer;
// 刪除性別為男性的講師
long num=queryFactory.delete(qLecturer)
.where(qLecturer.sex.eq(true))
.execute();
// 輸出被刪除的記錄數(shù)
System.out.println(num);
}
用例分析
從用例中可以看出其實QueryDSL的API更加切合原生的SQL,基本上從代碼上就可以看出你希望執(zhí)行的SQL了。
細(xì)心的朋友會發(fā)現(xiàn)QueryDSL是沒有insert方法,因為JPA提供的save()方法已經(jīng)足夠處理了。
同時要記得要組裝好你的SQL后別忘記調(diào)用fetch()或者execute()方法。
總結(jié)
Spring Boot JPA整合QueryDSL的關(guān)鍵步驟
- 引入依賴和插件
- 編寫配置類
- 使用插件生成代碼
- 標(biāo)記生成文件為代碼
- Repository繼承QuerydslPredicateExecutor
QueryDSL的API類似原生SQL,API風(fēng)格類似StringBuilder的API(Fluent API風(fēng)格)。但是不提供insert對應(yīng)的操作。
QueryDSL對于復(fù)雜的SQL的支持十分友好,算是對于JPA對這塊需求的補充和完善。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot項目中枚舉類型字段與前端和數(shù)據(jù)庫的交互方法
最近做的這個項目中,用到了大量的枚舉類,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項目中枚舉類型字段與前端和數(shù)據(jù)庫的交互方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
EL表達(dá)式的隱式對象_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了EL表達(dá)式的隱式對象,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07

