SpringBoot整合Spring-Data-Jpa + QueryDsl以及使用案例


這些年我接觸/學習過得ORM框架或庫也有一籮筐了。

  • dbutils
  • mybatis
  • sql2o
  • beetlsql
  • hibernate
  • cayenne
  • spring-data-jpa
  • querydsl

我覺得springboot應用中最得心應手的利器,還是 spring-data-jpa + queryds。但是它好像在國內不怎么流行,看國內的開源項目,工作遇到的項目基本都是mybatis/mybatis-plus。寫不完的xml和mapper,用不完的代碼生成。

這種單表CRUD的ORM框架,不能靈活的JOIN,投影查詢。新增一個JOIN表,就要新寫一個mapper方法和xml,新增一個查詢列,也要新寫一個mapper方法和xml(當然,我看到很多人很多人永遠都是SELECT * 干到底,一個 findOne 方法,哪里都可以用)。還要配置各種結果集映射。實在是太累了。

前陣子看到JEECMS居然用的就是QueryDsl,我就想着寫一個教程。也不能算是教程,只能算是一堆案例,QueyDsl的各種使用案例。如果你對QueryDsl一無所知,也可以直接看看。它並不難,你只要會寫SQL語句,那就會用了90%

QueyDsl

快速的解釋一下這玩意兒咋用,QueryDsl需要配置JPA使用,它根據你定義的JPA Entity實體類,逆向的生成查詢類。通過操作查詢類完成SQL的操作。

逆向生成的過程,完全自動,只需要配置好maven插件,定義好實體類就行。

怎么去操作這些查詢類?你會寫SQL就會操作。

它能完成項目中的大部分SQL查詢,太復雜了也沒轍。但是可以用spring-data-jpa的原生查詢。

快速看一眼

實體類 User 定義

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.With;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@With

@Entity
@Table(name = "user", uniqueConstraints = {
	@UniqueConstraint(columnNames = "name", name = "name")
}, indexes = {
	@Index(columnList = "department_id", name = "department_id")
})
@org.hibernate.annotations.Table(appliesTo = "user", comment = "用戶")
public class User implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1691873956126863400L;
	
	@Id
	@Column(columnDefinition = "INT UNSIGNED COMMENT 'ID'")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(columnDefinition = "VARCHAR(50) COMMENT '名字'", nullable = false)
	private String name;
	
	@Column(columnDefinition = "VARCHAR(10) COMMENT '性別'", nullable = false)
	@Enumerated(EnumType.STRING)
	private Gender gender;
	
	@Column(columnDefinition = "DECIMAL(10,2)COMMENT '賬戶余額'")
	private BigDecimal balance;
	
	@Column(name = "department_id", columnDefinition = "INT UNSIGNED COMMENT '部門ID'", nullable = false)
	private Integer departmentId;
	
	@Column(columnDefinition = "TINYINT UNSIGNED COMMENT '是否啟用。0:禁用,1:啟用'", nullable = false)
	private Boolean enabled;
	
	@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間'", nullable = false)
	private LocalDateTime createAt;
	
	@Column(columnDefinition = "TIMESTAMP DEFAULT NULL COMMENT '修改時間'")
	private LocalDateTime updateAt;	
	
	public static enum Gender {
		MALE,		// 男
		FEMALE	// 女
	}
}

QueryDsl自動生成的查詢類

import static com.querydsl.core.types.PathMetadataFactory.*;

import com.querydsl.core.types.dsl.*;

import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;


/**
 * QUser is a Querydsl query type for User
 */
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QUser extends EntityPathBase<User> {

    private static final long serialVersionUID = 373632107L;

    public static final QUser user = new QUser("user");

    public final NumberPath<java.math.BigDecimal> balance = createNumber("balance", java.math.BigDecimal.class);

    public final DateTimePath<java.time.LocalDateTime> createAt = createDateTime("createAt", java.time.LocalDateTime.class);

    public final NumberPath<Integer> departmentId = createNumber("departmentId", Integer.class);

    public final BooleanPath enabled = createBoolean("enabled");

    public final EnumPath<User.Gender> gender = createEnum("gender", User.Gender.class);

    public final NumberPath<Integer> id = createNumber("id", Integer.class);

    public final StringPath name = createString("name");

    public final DateTimePath<java.time.LocalDateTime> updateAt = createDateTime("updateAt", java.time.LocalDateTime.class);

    public QUser(String variable) {
        super(User.class, forVariable(variable));
    }

    public QUser(Path<? extends User> path) {
        super(path.getType(), path.getMetadata());
    }

    public QUser(PathMetadata metadata) {
        super(User.class, metadata);
    }

}

查詢Demo

JPAQueryFactory query = new JPAQueryFactory(this.entityManager);
		
QUser qUser = QUser.user;	// 生成的查詢對象,可以理解為數據表
		
User user = query.select(qUser).from(qUser).where(qUser.id.eq(1)).fetchOne();  // 查詢唯一記錄,如果結果不止一個則異常

有感覺了沒?用Java代碼的方式寫SQL。用代碼的方式進行JOIN檢索,投影查詢,結果集封裝。實在是太靈活。通過合理的抽象設計,直接在Controller就能把SQL執行了。

由於是根據實體類生成的查詢對象,那么在修改了實體類的字段名稱后,查詢對象會重新生成。在代碼中涉及到修改/刪除字段的相關操作都會在編譯時異常。而不是xml一樣,得去挨個找,甚至沒法找。不知道哪些人在哪些xml中用了這個被修改的字段。

官方地址

官網

https://querydsl.com/

Github

https://github.com/querydsl/querydsl

QueryDsl Example

案例有10來個,不方便在一篇帖子里面展開,所以新建了一個工程在Github。這個工程整合了spring-data-jpa 和 querydsl以及一些常用的案例。

https://github.com/KevinBlandy/springboot-querydsl-example

軟件版本

  • SpringBoot 2.6.1
  • Java 17
  • MYSQL 8.x

需要手動創建數據庫(看yaml配置),系統啟動會后自動創建數據表(包括索引)。

Example代碼

都在 src/main/resources 目錄下,可以每一個都執行一下看看,希望你會喜歡這玩意兒。

  • DataInit 初始化演示數據(最先執行)
  • Example1 單表的查詢/編輯/刪除
  • Example2 join查詢
  • Example3 分頁/排序
  • Example4 條件列子查詢/查詢列子查詢/exists子查詢/count子查詢
  • Example5 聚合查詢
  • Example6 條件分組
  • Example7 加鎖
  • Exapmle8 結果集封裝
  • Exapmle9 結果列的一些操作。case/轉換/null判斷...
  • Exapmle10 spring-data-jpa 的支持

最后,非常歡迎大家指出代碼中的問題,提出相關建議。


首發:https://springboot.io/t/topic/4424


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM