来自官网的介绍(翻译版)
前言 Querydsl是一个框架,可用于构造静态类型的类似SQL的查询。可以通过诸如Querydsl之类的流畅API构造查询,而不是将查询编写为内联字符串或将其外部化为XML文件。
例如,与简单字符串相比,使用流利的API的好处是
在IDE中使用代码完成;会有代码提示和自动补全,较为高效
(几乎)语法安全;
可以安全地引用域类型和属性;可以直接使用领域模型进行操作,毕竟本质就是面向对象
更好地重构域类型的更改;
跟写SQL一样的方便;
1. 简介 1.1. 背景 Querydsl是出于以类型安全的方式维护HQL查询的需要而诞生的。HQL查询的增量构造需要String连接,并导致难以阅读的代码。通过纯字符串对域类型和属性的不安全引用是基于字符串的HQL构造的另一个问题。
随着域模型的不断变化,类型安全性在软件开发中带来了巨大的好处。域更改直接反映在查询中,而查询构造中的自动完成功能使查询构造更快,更安全。
用于Hibernate的HQL是Querydsl的第一种目标语言,但如今它支持JPA,JDO,JDBC,Lucene,Hibernate Search,MongoDB,Collections和RDFBean作为后端。
1.2. 原则 类型安全 是Querydsl的核心原则。查询是根据生成的反映查询类型的属性来构造的。函数/方法调用也以完全类型安全的方式构造。
一致性 是另一个重要原则。在所有实现中,查询路径和操作都是相同的,而且Query接口具有公共的基本接口。
要了解Querydsl查询和表达式类型的表达能力,请访问javadocs并进行探索com.querydsl.core.Query
,com.querydsl.core.Fetchable
以及com.querydsl.core.types.Expression
。
如果是初次使用QueryDSL的同学建议去这篇博客: SpringDataJPA+QueryDSL玩转态动条件/投影查询 ,
本文针探讨的是使用时遇到的一些问题
2. 拓展示例 1. Projections简化代码,使代码更优雅 使用Projections方法可以更简单更方便的返回自定义的参数属性
1 2 3 4 5 6 7 8 9 10 11 12 13 QHajOrderDetails orderDetails = QHajOrderDetails.hajOrderDetails; return jpaQueryFactory.select( Projections.bean( CommoditySalesDto.class , // 取别名,与CommoditySalesDto 实体中字段相同 orderDetails.number.sum().as("sales"), orderDetails.commodityNo ) ).from(orderDetails) .where(orderDetails.commodityNo.in(commodityNos)) .groupBy(orderDetails.commodityNo) .fetch();
Projections的bean方法第一个属性是要查询对象的泛型类,对象中orderDetails.“commodityNo”属性就是CommoditySalesDto对应属性,大小写相同。如属性不同时可以使用as来为指定结果集添加别名对应dto内属性。
2. 关联同一张表两次进行查询 有时遇到一些查询需要在同一张表关联查询两次或多次,知道在sql中怎么写,但是在querydsl中就不知道怎么下手了,方法其实很简单
1 2 3 4 5 6 7 QHajCommodityType type1 = new QHajCommodityType("type1" ); QHajCommodityType type2 = new QHajCommodityType("type2" ); return jPAQueryFactory.select(type2.id) .from(type1) .join(type2).on(type1.id.eq(type2.parentId)) .fetch();
创建对应对象和别名,这样关联查询时才会区分。
3. 格式化字段进行查询 1 2 3 4 5 6 7 8 9 10 QHajOrder order = QHajOrder.hajOrder; StringTemplate dateExpr = Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')" , order.createTime); return jpaQueryFactory.select(dateExpr,order.id.count()) .from(order) .orderBy(order.id.desc()) .groupBy(dateExpr) .limit(5 ) .fetch();
4. 可添加判断逻辑,根据业务需要拼接,在代码中书写更便捷 1 2 3 4 5 6 7 8 9 10 11 12 13 QHajOrder order = QHajOrder.hajOrder; JPAQuery<Integer> jpaQuery = jpaQueryFactory.select(order.id.sum()).from(order); Integer count; if (ObjectUtil.isNotNull(type)) { count = jpaQuery.where(order.status.eq(0 )).fetchOne(); } else { count = jpaQuery.where(order.status.eq(1 )).fetchOne(); } if (ObjectUtil.isNull(count)) { count = 0 ; } return count;
5. 分页处理 1 2 3 4 5 6 7 8 9 10 11 12 13 public List<HajOrder> getOrderListByPage (Pager page) { QHajOrder order = QHajOrder.hajOrder; QueryResults<HajOrder> queryResults = jpaQueryFactory .selectFrom(order) .where(order.status.eq(0 )) .offset((page.getCurrentPage() - 1 ) * page.getPageSize()) .limit(page.getPageSize()) .fetchResults(); page.setRecordTotal((int ) queryResults.getTotal()); return queryResults.getResults(); }
6.多数据源配置使用 如果你还没配置多数据源使用,可以参照这个博客 Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置
之前一直是单数据源使用,在多数据源中使用可能会报这样的错
1 2 3 4 5 6 java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: * is not mapped at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350 ) at com.sun.proxy.$Proxy119.createQuery(Unknown Source) at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:101 ) at com.querydsl.jpa.impl.AbstractJPAQuery.fetchResults(AbstractJPAQuery.java:211 )
配置多个实体管理器EntityManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @SpringBootApplication @EnableJpaAuditing public class ApiApplication { public static void main(String[] args) { SpringApplication.run(ThirdApiApplication.class, args); } /** * Spring管理JPAQueryFactory * 默认 * @param entityManager * @return */ @Bean public JPAQueryFactory jpaQueryFactory(@Qualifier("entityManagerPrimary") EntityManager entityManager) { return new JPAQueryFactory(entityManager); } /** * 新配置数据源wmsJpaQueryFactory * * @param entityManager * @return */ @Bean public JPAQueryFactory wmsJpaQueryFactory(@Qualifier("entityManagerSecondary") EntityManager entityManager) { return new JPAQueryFactory(entityManager); } }
业务使用中按如下操作就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Autowired private JPAQueryFactory jpaQueryFactory;@Autowired private JPAQueryFactory wmsJpaQueryFactory;public void test () { QCustomer customer = QCustomer.customer; Customer bob = wmsJpaQueryFactory.select(customer) .from(customer) .where(customer.firstName.eq("Bob" )) .fetchOne(); }
资料: github-querydsl资源
Querydsl官网
官网querydsl-jpa示例
Querydsl参考指南