JPA 之 QueryDSL-JPA 使用指南

Posted 墨鸦_Cormorant

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JPA 之 QueryDSL-JPA 使用指南相关的知识,希望对你有一定的参考价值。

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<T>的Spring-data风格。

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


依赖

 	<dependencies>
        <!-- QueryDSL框架依赖 -->
		<dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>
	</dependencies>

添加maven插件(pom.xml)

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

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

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

<project>
	<build>
        <plugins>
            <plugin> 
                <!--因为QueryDsl是类型安全的,所以还需要加上Maven APT plugin,使用 APT 自动生成Q类:-->
            	<groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <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>

补充:

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

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

<project>
	<build>
		<plugins>
			<plugin>
                <groupId>com.querydsl</groupId>
                <artifactId>querydsl-maven-plugin</artifactId>
                <version>$querydsl.version</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>export</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver>
                    <jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl>
                    <packageName>com.mycompany.mydomain</packageName>
                    <targetFolder>$project.basedir/target/generated-sources/java</targetFolder>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.derby</groupId>
                        <artifactId>derby</artifactId>
                        <version>$derby.version</version>
                    </dependency>
                </dependencies>
			</plugin>
		</plugins>
	</build>
</project>

生成查询实体

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

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

双击 clean 清除已编译的 target

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

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


JPAQueryFactory 风格

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

装配 与 注入

SpringBoot注解方式装配

/**
 * 方式一。使用Spring的@Configuration注解注册实例进行容器托管
 */
@Configuration
public class QueryDslConfig 
    @Bean
    public JPAQueryFactory jpaQueryFactory(EntityManager em)
        return new JPAQueryFactory(em);
    



/**
 * 方式二。在Dao类中初始化
 */
	// 实体管理
	@Autowired
	private 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
@Transactional
public 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
@Transactional
public 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的Predicate
T cases().when(Predicate).then(T a).otherwise(T b)
    
DateExpression<Date> currentDate()			// 返回当前日历(年-月-日)的 DateExpression
TimeExpression<Time> currentTime()			// 返回当前时刻(时:分:秒)的 TimeExpression
DateTimeExpression<Date> currentTimestamp()	// 返回当前时间(年-月-日 时:分:秒)的 DateTimeExpression
    
// exprs 均为名为eqTrue的Predicate ,则返回名为eqTrue的Predicate,否则返回eqFalse的Predicate
BooleanExpression allOf(BooleanExpression... exprs)
// exprs 至少存在一个名为eqTrue的Predicate,则返回名为eqTrue的Predicate,否则返回eqFalse的Predicate
BooleanExpression anyOf(BooleanExpression... exprs)

// 转类型为 BooleanExpression。特别注意:asBoolean(Boolean).isTrue() 才是可用Predicate
BooleanExpression 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<T> numberTemplate(Class<? extends T> cl, String template, Object... args)
BooleanTemplate booleanTemplate(String template, ImmutableList<?> args)

MathExpressions 数学表达式工具类

NumberExpression<A> round(Expression<A> num)			// 四舍五入取整
NumberExpression<A> round(Expression<A> num, int s)		// 四舍五入保留 s 位小数

NumberExpression<Double> asin(Expression<A> num)		// 返回num的反正弦值。-1 <= num <= 1,否则返回null
NumberExpression<Double> acos(Expression<A> num)		// 返回num的反余弦值。-1 <= num <= 1,否则返回null

// 慎用!qdsl-jpa底层是调用random()函数,mysql没有该函数,只有rand()函数,会报错,解决方案为使用QDSL-SQL查询
NumberExpression<Double> random()			// 返回0到1内的随机值
NumberExpression<Double> random(int seed)	// 返回一个指定的0到1内的随机值

表达式方法

注意:在select()中查询出的结果使用表达式方法处理过后,若要封装到实体类中,则都需要使用 .as(alias) 起别名指定封装到实体类中的哪个字段。

SimpleExpression 简单表达式 extends DslExpression extends Expression

// 给查询字段取别名
T as(alias)

BooleanExpression eq(T right)	// 等于 equal
BooleanExpression eqAll(T... right)
BooleanExpression eqAny(T... right)
BooleanExpression ne(T right)	// 不等于	not equal 
BooleanExpression neAll(T... right)
BooleanExpression neAny(T... right)

BooleanExpression in(T... right)
BooleanExpression notIn(T... right)
    
BooleanExpression isNotNull()
BooleanExpression isNull()
    
// 相当于java中的switch语句。两种写法
T when(A).then(B).otherwise(C)

// 该字段的查询结果等于参数则返回null,不等于则返回查询结果。Field == A ? null : Field
SimpleExpression<T> nullif(A)

// 符合过滤条件的的总条数。		select count(table.id) from table
NumberExpression<Long> count()

ComparableExpressionBase extends SimpleExpression

// 设置默认值。返回 Field, A, B ... 顺序中第一个非null的值,若都为null则返回null
// 注意:使用该方法兜底Oracle数据库的null为空字符串时会失效,因为Oracle会把空字符串当作null
T coalesce(A, B ...)

NumberExpression 数值表达式 extends ComparableExpressionBase

NumberExpression<T> add(A)			// 加    
NumberExpression<T> subtract(A)		// 减  
NumberExpression<T> multiply(A)		// 乘  
NumberExpression<T> divide(A)		// 除  
NumberExpression<T> mod(A)			// 返回余数
 
NumberExpression<T> floor()		// 向下取整
NumberExpression<T> ceil()		// 向上取整
NumberExpression<T> round()		// 四舍五入取整

NumberExpression<T> max()			// 返回指定数值列的最大值
NumberExpression<T> min()			// 返回指定数值列的最小值
NumberExpression<T> sqrt()			// 返回指定数值列的平方根

NumberExpression<T> sum()			// 返回指定数值列(或分组相同数值列)的总数
NumberExpression<T> avg()			// 返回指定数值列(或分组相同数值列)的平均数

NumberExpression<T> abs()			// 返回指定数值列的绝对值
NumberExpression<T> negate()		// 返回指定数值列的相反数

StringExpression stringValue()	// 返回字符串表达式
    
// 数据类型转换为数字类型。type为数字基本类型的包装类.class。实体类接收字段需与type的类型一致。
NumberExpression<T> castToNum(Class<A> type)

ComparableExpression extends ComparableExpressionBase

BooleanExpression lt(T right)	// 小于 less than
BooleanExpression ltAll(T... right)
BooleanExpression ltAny(T... right)
BooleanExpression gt(T right) 	// 大于 greater than
BooleanExpression gtAll(T... right)
BooleanExpression gtAny(T... right)

BooleanExpression loe(T right)	// 小于等于 less than or equal
BooleanExpression loeAll(T... right)
BooleanExpression loeAny(T... right)
BooleanExpression goe(T right)	// 大于等于 greater than or equal
BooleanExpression goeAll(T... right)
BooleanExpression goeAny(T... right)

BooleanExpression between(from, to)		// from和to之间  [from, to]
BooleanExpression notBetween(from, to)

BooleanExpression 布尔表达式 extends LiteralExpression (extends ComparableExpression) implements Predicate

BooleanExpression isTrue()		// 计算结果若为true,则返回名为eqTrue的Predicate,否则返回名为eqFalse的Predicate
BooleanExpression isFalse()		// 计算结果若为false,则返回名为eqTrue的Predicate,否则返回名为eqFalse的Predicate
BooleanExpression not()			// 返回相反的结果

BooleanExpression eq(Boolean right)

BooleanExpression and(Predicate right)
BooleanExpression andAnyOf(Predicate... predicates)
BooleanExpression or(Predicate right)
BooleanExpression orAllOf(Predicate... predicates)

StringExpressions 字符串表达式 extends LiteralExpression extends ComparableExpression

StringExpression contains(String str)	// 包含参数字符串
    
BooleanExpression isEmpty()		// 判断是否为空
BooleanExpression isNotEmpty()	
    
// 正则匹配查询
BooleanExpression matches(Expression<String> regex)
// 模糊查询。% 为通配符,_ 表一个字符,可以传参escape指定转义字符
BooleanExpression like(String str)
BooleanExpression like(String str, char escape)
BooleanExpression endsWith(str)		// 判断字符串的后缀是否为str。注意:必须使用boolean数据类型的字段接收
BooleanExpression startsWith(str)	// 判断字符串的前缀是否为str。注意:必须使用boolean数据类型的字段接收
      
// 将字母转换大小写
StringExpression toLowerCase()
StringExpression toUpperCase()
StringExpression lower()
StringExpression upper()
    
StringExpression trim()			// 去掉字符串两端的空格
StringExpression substring(int beginIndex)		// 截取子字符串从索引位置至末尾
StringExpression concat(str)	// 拼接 str
StringExpression append(str)	// 在末尾添加 str
StringExpression prepend(str)	// 在前面添加 str

NumberExpression<Integer> length()			// 返回字符串长度
NumberExpression<Integer> locate(str)		// 返回 str 的位置(从1开始),没有返回0
NumberExpression<Integer&g

以上是关于JPA 之 QueryDSL-JPA 使用指南的主要内容,如果未能解决你的问题,请参考以下文章

QueryDSL 4 与 RowNumber Window 功能

spring-data-jpa 如何使用多个数据源? [复制]

JPA的理解

Java应用如何使用JPA进行对象关系映射和持久化

flea-db使用之JPA分库分表实现

flea-db使用之JPA分库分表实现