zoukankan      html  css  js  c++  java
  • SpringBoot整合SpringDataJpa + 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

  • 相关阅读:
    c#序列化和反序列化list
    centos7安装oracle 11gr2
    centos7系统备份与还原
    FAT32和NTFS最大支持的单个文件大小分别是多大?
    linux挂载远程windows服务器上的ISO,给内网的服务器安装软件
    tomcat8启动慢原因及解决办法
    CentOS统的7个运行级别的含义
    对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
    空侃简介
    linux环境部署
  • 原文地址:https://www.cnblogs.com/kevinblandy/p/15632739.html
Copyright © 2011-2022 走看看