小工具知识网

当前位置: 首页 > 行业知识 > JPA 之 QueryDSL-JPA 使用指南

行业知识

JPA 之 QueryDSL-JPA 使用指南

2026-11-07 19:26:52 来源:互联网转载

Querydsl-JPA 框架(推荐)

官���:传送门

参考:

  • JPA整合Querydsl入门篇
  • SpringBoot环境下QueryDSL-JPA的入门及进阶

    概述及依赖、插件、生成查询实体

    1.Querydsl支持代码自动完成,因为是纯Java API编写查询,因此主流Java IDE对起的代码自动完成功能支持几乎可以发挥到极致(因为是纯Java代码,所以支持很好)

    2.Querydsl几乎可以避免所有的SQL语法错误(当然用错了Querydsl API除外,因为不写SQL了,因此想用错也难)

    3.Querydsl采用Domain类型的对象和属性来构建查询,因此查询绝对是类型安全的,不会因为条件类型而出现问题

    4.Querydsl采用纯Java API的作为SQL构建的实现可以让代码重构发挥到另一个高度

    5.Querydsl的领一个优势就是可以更轻松的进行增量查询的定义

    使用

    在Spring环境下,可以通过两种风格来使用QueryDSL。

    一种是使用JPAQueryFactory的原生QueryDSL风格, 另一种是基于Spring Data提供的QueryDslPredicateExecutor的Spring-data风格。

    使用QueryDslPredicateExecutor可以简化一些代码,使得查询更加优雅。 而JPAQueryFactory的优势则体现在其功能的强大,支持更复杂的查询业务。甚至可以用来进行更新和删除操作。

    依赖

                         com.querydsl            querydsl-apt                            com.querydsl            querydsl-jpa        

    添加maven插件(pom.xml)

    添加这个插件是为了让程序自动生成query type(查询实体,命名方式为:“Q”+对应实体名)。

    上文引入的依赖中querydsl-apt即是为此插件服务的。

    注:在使用过程中,如果遇到query type无法自动生成的情况,用maven更新一下项目即可解决(右键项目->Maven->Update Project)。

                                                     com.mysema.maven                apt-maven-plugin                1.1.3                                                            generate-sources                                                    process                                                                            target/generated-sources/java                            com.querydsl.apt.jpa.JPAAnnotationProcessor                                                                            

    补充:

    QueryDSL默认使用HQL发出查询语句。但也支持原生SQL查询。

    若要使用原生SQL查询,你需要使用下面这个maven插件生成相应的query type。

                    com.querydsl                querydsl-maven-plugin                ${querydsl.version}                                                                                        export                                                                                                org.apache.derby.jdbc.EmbeddedDriver                    jdbc:derby:target/demoDB;create=true                    com.mycompany.mydomain                    ${project.basedir}/target/generated-sources/java                                                                            org.apache.derby                        derby                        ${derby.version}                                    

    生成查询实体

    idea 工具 为maven project 自动添加了对应的功能。添加好依赖和 plugin 插件后,就可以生成查询实体了。

    打开右侧的 Maven Projects,如下图所示:

    双击 clean 清除已编译的 target

    双击 compile 命令执行,执行完成后会在 pom.xml 配置文件内配置生成目录内生成对应实体的 QueryDSL 查询实体。

    生成的查询实体如下图所示:

    JPAQueryFactory 风格

    QueryDSL 在支持JPA的同时,也提供了对 Hibernate 的支持。可以通过 HibernateQueryFactory 来使用。

    装配 与 注入

    SpringBoot注解方式装配

    /** * 方式一。使用Spring的@Configuration注解注册实例进行容器托管 */@Configurationpublic class QueryDslConfig {    @Bean    public JPAQueryFactory jpaQueryFactory(EntityManager em){        return new JPAQueryFactory(em);    }}/** * 方式二。在Dao类中初始化 */// 实体管理@Autowiredprivate EntityManager entityManager;// 查询工厂private JPAQueryFactory queryFactory;// 初始化JPA查询工厂@PostConstruct// Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)public void init(){        queryFactory = new JPAQueryFactory(entityManager);    }

    注入

        @Autowired    private JPAQueryFactory queryFactory;

    更新、删除

    JPAQueryFactory 更新

    在Querydsl JPA中,更新语句是简单的 update-set/where-execute 形式。

    execute()执行后返回的是被更新的实体的数量。

    注意:使用QueryDsl更新实体时需要添加事务

    @Test@Transactionalpublic void testUpdate() {    QStudent qStudent = QStudent.student;    Long result = queryFactory.update(qStudent)            .set(qStudent.name, "haha")// 可以用if条件判断更新值来确定字段是否.set()      .setnull(qStudent.age)// 设置null值            .where(qStudent.id.eq(111L)).execute();    assertThat(result, equalTo(1L));}

    JPAQueryFactory 删除

    删除语句是简单的 delete-where-execute 形式。

    注意:使用QueryDsl删除实体时需要添加事务

    @Test@Transactionalpublic void testDelete() {    QStudent qStudent = QStudent.student;    //删除指定条件的记录    Long result = queryFactory.delete(qStudent)            .where(qStudent.id.eq(111L))            .execute();    assertThat(result, equalTo(1L));        //删除所有记录。即不加where条件    Long totalResult = queryFactory.delete(qStudent).execute();    System.out.println(totalResult);}

    查询

    表达式工具类

    Expressions 表达式工具类

    // when-then 条件表达式函数。when传参必须为名为eqTrue或eqFalse的PredicateT cases().when(Predicate).then(T a).otherwise(T b)    DateExpression currentDate()// 返回当前日历(年-月-日)的 DateExpressionTimeExpression currentTime()// 返回当前时刻(时:分:秒)的 TimeExpressionDateTimeExpression currentTimestamp()// 返回当前时间(年-月-日 时:分:秒)的 DateTimeExpression    // exprs 均为名为eqTrue的Predicate ,则返回名为eqTrue的Predicate,否则返回eqFalse的PredicateBooleanExpression allOf(BooleanExpression... exprs)// exprs 至少存在一个名为eqTrue的Predicate,则返回名为eqTrue的Predicate,否则返回eqFalse的PredicateBooleanExpression anyOf(BooleanExpression... exprs)// 转类型为 BooleanExpression。特别注意:asBoolean(Boolean).isTrue() 才是可用PredicateBooleanExpression asBoolean(Boolean)// asBoolean(true)    booleanPath("true")NumberExpression asNumber(T)StringrExpression asString(T)DateExpression asDate(T)TimeExpression asTime(T)DateTimeExpression asDateTime(T)// 自定义语法StringTemplate stringTemplate(String template, Object... args)NumberTemplate numberTemplate(Class args)

    MathExpressions 数学表达式工具类

    NumberExpression round(Expression num)// 四舍五入取整NumberExpression round(Expression num, int s)// 四舍五入保留 s 位小数NumberExpression asin(Expression num)// 返回num的反正弦值。-1         //商品基本信息        QGoodInfoBean goodBean = QGoodInfoBean.goodInfoBean;        //商品类型        QGoodTypeBean goodTypeBean = QGoodTypeBean.goodTypeBean;        return queryFactory                    .select(                        goodBean.id,                        goodBean.price,                        goodTypeBean.name,                        goodTypeBean.id)                    .from(goodBean,goodTypeBean)//构建两表笛卡尔集                    .where(goodBean.typeId.eq(goodTypeBean.id))//关联两表                    .orderBy(goodBean.order.desc())//倒序                    .fetch()                    .stream()                    //转换集合内的数据                    .map(tuple - {                        //创建商品dto                        GoodDTO dto = new GoodDTO();                        //设置商品编号                        dto.setId(tuple.get(goodBean.id));                        //设置商品价格                        dto.setPrice(tuple.get(goodBean.price));                        //设置类型编号                        dto.setTypeId(tuple.get(goodTypeBean.id));                        //设置类型名称                        dto.setTypeName(tuple.get(goodTypeBean.name));                        //返回本次构建的dto                        return dto;                    })                    //返回集合并且转换为List        List        QUserAddress ua = QUserAddress.userAddress;        QUser u = QUser.user;        List        QTCity qtCity = QTCity.tCity;        QTHotel qtHotel = QTHotel.tHotel;                JPAQuery            System.out.println("qtCity:" + row.get(qtCity));            System.out.println("qtHotel:" + row.get(qtHotel));            System.out.println("--------------------");        }    }    private Integer id;    private String username;    private String password;    private String phone;    private List}... exprs) 手动封装结果集。 
    /** * extends RelationalPathBase 的Q类示例 */public class QEmployee extends RelationalPathBase {    private static final long serialVersionUID = 1394463749655231079L;    public static final QEmployee employee = new QEmployee("EMPLOYEE");    public final NumberPath id = createNumber("id", Integer.class);    public final StringPath firstname = createString("firstname");    public final DatePath datefield = createDate("datefield", java.util.Date.class);    public final PrimaryKey idKey = createPrimaryKey(id);    public QEmployee(String path) {        super(Employee.class, PathMetadataFactory.forVariable(path), "PUBLIC", "EMPLOYEE");        addMetadata();    }    public QEmployee(PathMetadata metadata) {        super(Employee.class, metadata, "PUBLIC", "EMPLOYEE");        addMetadata();    }    protected void addMetadata() {        addMetadata(id, ColumnMetadata.named("ID").ofType(Types.INTEGER));        addMetadata(firstname, ColumnMetadata.named("FIRSTNAME").ofType(Types.VARCHAR));        addMetadata(datefield, ColumnMetadata.named("DATEFIELD").ofType(Types.DATE));    }}
    /** * extends EntityPathBase 的Q类示例 */public class QUserAddressS extends EntityPathBase {    private static final long serialVersionUID = -1295712525L;    public static final QUserAddressS userAddress = new QUserAddressS("tb_user_address");        public final NumberPath id = createNumber("id", Integer.class);    public final StringPath address = createString("address");    public final DateTimePath createTime = createDateTime("create_time", java.util.Date.class);    public QUserAddressS(String variable) {        super(UserAddress.class, forVariable(variable));    }    public QUserAddressS(Path expr, String delimiter)// 使用示例:SQLExpression.listagg(qEntity.name, ",").withinGroup.OrderBy(qEntity.name.asc()).getValue.as("Name")// 将多列记录聚合为一列记录。separator为分隔符。MySQL、PostgreSQL都可用,PostgreSQL会根据模板翻译成String_agg函数static StringExpression groupConcat(Expression expr, String separator)static StringExpression groupConcat(Expression expr)    static  RelationalFunctionCall relationalFunctionCall(Class dateTime)    static  DateTimeExpression dateadd(DatePart unit, DateTimeExpression date, int amount)static  DateExpression dateadd(DatePart unit, DateExpression date, int amount)// 获取两个日期的时间间隔(end-start)static  NumberExpression datediff(DatePart unit, DateTimeExpression start, DateTimeExpression end)

    JPASQLQuery 方式

    使用 JPASQLQuery 作为查询引擎时,使用的QEntity(extends EntityPathBase),传入构造方法的 variable 参数可以不为数据库表名(因为 JPASQLQuery 可以找到映射的真实表名,仅把此参数作为表别名),但所有的 property 参数仍必需为相对应的数据库字段名。

    故并不能直接使用 apt 插件生成 的 jpa 使用的 Q类,仍需要使用改造版的 Q类(extends EntityPathBase)。

    @Test    public void selectBySqlQueryFactory(){        // 使用 extends EntityPathBase 的改造版QEntity,结果集如需封装到实体类,必须手动指定实体类来接收        QUserAddress ua = QUserAddress.userAddress;        // jpa+sql的查询工具,本例使用的oracle的sql模板        JPASQLQuery jpasqlQuery = new JPASQLQuery(em, new OracleTemplates());        // 子查询        SQLQuery q = SQLExpressions            .select(                // 查询字段须是数据库表中的字段名(不是实体属性名),且类型一致。如直接不使用QEntity的属性,则需手动指定                Expressions.stringPath("addressee").as("addressee")                , Expressions.numberPath(Integer.class, "user_id").as("user_id")        )            .from(ua);        List fetch = jpasqlQuery            .select(                // 查询字段须是临时表中的字段名或别名,且类型一致。结果集字段需添加别名手动映射封装                Expressions.template(String.class, "q.addressee").as("addressee")                , Expressions.numberTemplate(Integer.class, "q.user_id").as("userId")        )            .from(q, Expressions.stringPath("q"))   // 子查询作为临时表            .fetch();        System.out.println(fetch);    }

上一篇:麦块服务器怎么造?

下一篇:小程序怎么访问服务器?