行业知识
JPA 之 QueryDSL-JPA 使用指南
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); }
上一篇:麦块服务器怎么造?
下一篇:小程序怎么访问服务器?