阅读 753

QueryDSL使用范例

序言

由于项目可能部署到不同的数据库类型,使用mybatis操作简单,做不到一套SQL全部类型数据库可用(不求100%完美可用,但求尽可能少的修改)。目前部署的数据库类型有:

  • MySQL5.7
  • MySQL8.0.x
  • Oracle9x
  • Oracle10
  • Oracle11g
  • Oracle12c
  • Oracle19c
  • DB2-11.1.4
  • DB2-11.1.5
  • Postgres9
  • Postgres12

如果只是遇到MySQL和Oracle还好点,一下涉及到这么多数据库类型,然后不同版本数据库可能还有点语法不太一样,使用MyBatis无疑会增加测试,开发的工作量,效率底下,本身不要求高并发,所以采用JPA的方式确实能减少很多工作,并且效率很高,QueryDSL更是可以为JPA插上翅膀,方便很多工作。

QueryDSL

官网地址

www.querydsl.com/static/quer… 最新的官方文档

搭建

1.引入依赖

<!--下面这些配置要配置到 @Entity注解所在的模块中的pom.xml里面,配置到其他的项目不行-->
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
  <version>${querydsl.version}</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
  <version>${querydsl.version}</version>
</dependency>

<project>
  <build>
  <plugins>
    ...
    <plugin>
      <groupId>com.mysema.maven</groupId>
      <artifactId>apt-maven-plugin</artifactId>
      <version>1.1.3</version>
      <executions>
        <execution>
          <goals>
            <goal>process</goal>
          </goals>
          <configuration>
            <outputDirectory>target/generated-sources/java</outputDirectory>
            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
          </configuration>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
  </build>
</project>
复制代码

2.生成Qxxx类

引入QueryDSL依赖包之后,需要点击下maven构建,生成以Q开头的查询class

同步QueryDSL.png

Maven这两个箭头,随便点击一个就行,结果如下图:

generateclass.png

好奇心查看下生成的代码例子:

@Generated("com.querydsl.codegen.EntitySerializer")
public class QPermission extends EntityPathBase<Permission> {

    private static final long serialVersionUID = 1115934067L;

    public static final QPermission permission = new QPermission("permission");

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

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

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

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

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

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

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

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

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

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

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

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

    public final EnumPath<com.xuanwu.fgmp.common.enumeration.base.PermissionType> permissionType = createEnum("permissionType", com.xuanwu.fgmp.common.enumeration.base.PermissionType.class);

    public final EnumPath<com.xuanwu.fgmp.common.enumeration.base.PlatformType> platformFlag = createEnum("platformFlag", com.xuanwu.fgmp.common.enumeration.base.PlatformType.class);

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

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

    public QPermission(String variable) {
        super(Permission.class, forVariable(variable));
    }

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

    public QPermission(PathMetadata metadata) {
        super(Permission.class, metadata);
    }

}
复制代码

可以看到生成的都是些xxxxPath的对象,这个是为了后面组件JPAQuery做准备的!

使用

QueryDSL目前只支持:Update,Delete,Select操作,不支持Save操作,但是看官方文档有insert操作,这个目前没去试验!

准备两个表

/**
 * 我们使用了lombok,只给出主键注解,其他省略了,代码太多了
 * 这个是角色表
 */
@Entity
@Table(name = "role")
@Data
@Accessors(chain = true)
public class Role {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private String remark;
    private Boolean isDefault;
    private Integer userId;
    private Date createTime;
    private Date updateTime;
}
复制代码
/**
 * 用户信息表 无关的属性全给删除了
 */
@Entity
@Table(name = "user")
@Data
@Accessors(chain = true)
public class BaseUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Comment(comment = "主键,自增id")
    private Integer id;
    private Integer enterpriseId;
    private String name;
    private String account;
    @Convert(converter = UserStateConverter.class)
    private UserState state;
    private Integer departmentId;
    private Date createTime;
    private Date updateTime;

}

复制代码
//角色和用户关联表,用户---多对多---角色 的关系
@Entity
@Table(name = "fgmp_user_role")
public class UserRole {
    private Integer userId;
    private Integer roleId;
}
复制代码

构造JPAQueryFactory

@Configuration
public class JPAQueryFactoryConfig {
    @Autowired
    @PersistenceContext
    private EntityManager entityManager;

    @Bean("jpaQueryFactory")
    public JPAQueryFactory jpaQueryFactory(){
        return new JPAQueryFactory(entityManager);
    }
}
复制代码

单表操作

update

@Transactional
@Override
public boolean modifyUserById(Integer userId) {
    QBaseUser user = QBaseUser.baseUser;//这个是配置QueryDSL之后自动生成的
    return jpaQueryFactory.update(user)
            .set(user.isRemove, true)
            .set(user.name,"xxxx修改的名字信息")
            .set(user.updateTime, new Date())
            .where(user.id.eq(userId))//条件
            .execute() == 1;
}

翻译成SQL:
update user
set name = xxx,is_remove=true,update_time='xxxxx日期'
where id = xxxx;
复制代码

delete

@Transactional
@Override
public boolean deleteUserById(Integer userId) {
    QBaseUser user = QBaseUser.baseUser;//这个是配置QueryDSL之后自动生成的
    return jpaQueryFactory.delete(user)
            .where(user.id.eq(userId))//条件
            .execute() == 1;
}

翻译成SQL

delete from user 
where id = xxx;
复制代码

select

public void pageData(){
    QBaseUser user = QBaseUser.baseUser;
    List<BaseUser> pageDate = jpaQueryFactory.selectFrom(user)
            .where(user.updateTime.between(new Date(), new Date()))
            .where(user.isRemove.eq(false).or(user.id.gt(3)))
            .orderBy(user.createTime.desc())
            .offset(3)
            .limit(10)
            .fetch();
}

翻译成SQL(这个而是按照MySQL写的,其他数据库可能不一样):
select *
from user
where update_time between 日期1 and 日期2
      and (is_remove = false or id >3)
order by create_time desc
limint 3,10;

注意,连着写.where(xxxx).where(xxxx)默认是and的关系,当然你可以这么写

.where(user.updateTime.between(new Date(), new Date()).and(user.isRemove.eq(false).or(user.id.gt(3))))
==
.where(user.updateTime.between(new Date(), new Date()))
.where(user.isRemove.eq(false).or(user.id.gt(3)))
复制代码

处理部分字段返回结果

public void pageData(){
    QBaseUser user = QBaseUser.baseUser;
    List<Tuple> tuples = jpaQueryFactory.select(user.account, user.name)
            .from(user)
            .where(user.id.gt(13))
            .fetch();

    List<SimpleUser> result = tuples.stream()
            .map(tuple -> new SimpleUser()
                    .setAccount(tuple.get(user.account))//注意这个地方只能从上面select的字段才有数据
                    .setName(tuple.get(user.name)))
            .collect(Collectors.toList());
}

@Data
@Accessors(chain = true)
class SimpleUser{
    private String account;
    private String name;
}
复制代码

连表操作

这个才是业务当中的重中之重

 List<Tuple> result = jpaQueryFactory.select(user.id,
                user.account,
                user.name,
                user.email,
                department.name,
                user.state)
                .from(user)
                .leftJoin(department)
                .on(user.departmentId.eq(department.id))
                .where(user.isRemove.eq(false))
                .where(department.path.startsWith('xxx').or(department.id.eq(xid)))
                .fetch();
                
 翻译成SQL:
 select user.id,user.account,user.name,user.email,department.name,user.state
 from user left join department on user.department_id = department.id
 where user.is_remove = fasle
       and (department.path like '%xxx%' or department.id=xid);
       
       
 这个返回结果也是List<Tuple> 自己像上面转化下就行!
复制代码

子查询

List<LoginToken> result = queryFactory.select(loginToken)
                .where(loginToken.id.eq(JPAExpressions.select(app.id.max()).from(app)))
                .fetch();
                
翻译成SQL:
select *
from loginToken
where loginToken.id = (select max(app.id)
from app);
复制代码
文章分类
后端
文章标签