spring boot整合jpa注意点之QueryDsl框架使用
yaml 的配置
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 密码
url: 数据库地址
jpa:
# 控制台显示sql语句,一般很难看
show-sql: true
hibernate:
# 项目启动时,根据实体类怎么变更数据库的表
ddl-auto: update
其中:spring.jpa.hibernate.ddl-auto,可选参数:
- create:每次运行程序时,都会重新创建表,故而数据会丢失
- create-drop:每次运行程序时会先创建表结构,然后程序结束时清空表
- update:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)
- validate:运行程序会校验数据于数据库的字段类型是否相同,字段不同会报错
- none:禁用DDL处理
实体类注解配置讲解
- @Entity:
注解表示该类是一个实体类,在项目启动时会根据该类自动生成一张表,表的名称即@Entity注解中name的值,如果不配置name,默认表名为类名
-
指定实体名称(表名):
-
没有指定name属性且没有使用@Table,命名为类名生成
-
指定name属性且没有使用@Table,命名为name属性value值
-
指定name属性且使用了@Table指定name,命名以@Table的name的value
- @Table:
注解用来标识实体类与数据表的对应关系。
- name:表示该实体类映射的表名。
- catalog:指定数据库名称,默认为当前连接url配置的数据库。
- schema:指定数据库的用户名 ,默认为当前配置的用户。
- uniqueConstraints:用于批量设置唯一约束。
- @Id:
所有的实体类都要有的主键,@Id注解表示该属性是一个主键
- @GeneratedValue:
注解表示主键自动生成,strategy
则表示主键的生成策略
JPA自带的几种主键生成策略:
- TABLE:使用一个特定的数据库表格来保存主键
- SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要与generator一起使用,generator指定生成主键的生成器
- IDENTITY:主键由数据库自动生成(主要支持自动增长的数据库,如mysql)
- AUTO:主键由程序控制,也是GenerationType的默认值,mysql不支持,会报错:test.hibernate_sequence不存在
- @GenericGenerator:
自定义主键生成策略,由@GenericGenerator实现。
- name属性指定生成器名称。 与@GeneratorValue中 generator 的值对应。
- strategy属性指定具体生成器的类名。
- parameters得到strategy指定的具体生成器所用到的参数。
*Hibernate主键生成策略和各自的具体生成器之间的关系,在IdentifierGeneratorFactory接口中已经定义了。由DefaultIdentifierGeneratorFactory工厂去实现的
几种常见的主键策略生成器:
native:对于 oracle 采用 Sequence 方式;对于 MySQL 和 SQL Server 采用 Identity 方式。native就是将主键的生成工作交由数据库完成,hibernate不管。
uuid:采用128位的uuid算法生成主键,uuid被编码为一个32位16进制数字的字符串,占用空间大。
guid:采用数据库底层的guid算法机制,对应MYSQL的uuid()函数,SQL Server的newid()函数,ORACLE的rawtohex(sys_guid())函数等。
assigned: 在插入数据的时候主键由程序处理,等同于JPA中的AUTO,这是@GenericGenerator的默认设置。
6.@Column:
@Column注解来标识实体类中属性与数据表中字段的对应关系。
查看@Column源码发现@Column注解共有10个属性,且均为可选属性。
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
//name属性定义了被标注字段在数据库表中所对应字段的名称。
String name() default "";
//unique属性表示该字段是否为唯一标识,默认为false.
boolean unique() default false;
//nullable属性表示该字段是否可以为null值,默认为true。
boolean nullable() default true;
//insertable属性表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。
//insertable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
boolean insertable() default true;
//updatable属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。
//updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。
boolean updatable() default true;
//columnDefinition属性表示创建表时,该字段创建的SQL语句
String columnDefinition() default "";
//table属性定义了包含当前字段的表名。
String table() default "";
//length属性表示字段的长度,当字段的类型为varchar时,该属性才有效。
int length() default 255;
//precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。
int precision() default 0;
int scale() default 0;
}
- @Transient:注解表示在生成数据库的表时,该属性被忽略,即不生成对应的字段
- 代码展示
@Data
@Entity
@Table(name = "sys_user")
public class SysUser {
@Id
@GeneratedValue(strategy = GenerationType.AUTO,generator = "user-id")
@GenericGenerator(name = "user-id",strategy = "uuid")
private String id;
@Column(name = "user_name")
private String name;
@Column(name = "user_author")
private String author;
private Float price;
@Transient
private String description;
}
dao层接口实现
通过集成jpa的接口实现自己项目中的持久层接口,继承JpaRepository接口
import com.yc.pojo.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
}
其中JpaRepository又继承了PagingAndSortingRepository和QueryByExampleExecutor这两个接口。
在这几个接口中已经实现了我们常用的几种增删改查方法。并且实现这个接口后可以编写派生的方法去做sql的增删改查。
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
// 查询等于name的所有数据
List<SysUser> findByName(String name);
// 查询price不为空的数据
List<SysUser> findByPriceNotNull();
//查询既有name,又有author相等的数据
List<SysUser> findByNameAndAuthor(String name,String author);
}
注意:如果我们使用这种派生的方法去做sql的操作,必须遵循jpa的模板。例如下面
关键词 | 样本 | JPQL 片段 |
---|---|---|
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is ,Equals | findByFirstname , findByFirstnameIs ,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull ,Null | findByAge(Is)Null | … where x.age is null |
IsNotNull ,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (参数绑定附加% ) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (参数绑定在前面% ) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (参数绑定在 中% ) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
特殊参数处理
在自定义从查询分页或排序的情况下,我们通常可以自己使用Pageable
、Slice
和Sort
。
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
注意Sort
并Pageable
不能传入null值,如果针对这个方法,你不想进行分页或者排序,请使用Sort.unsorted()
和Pageable.unpaged()
。
分页和排序
您可以使用属性名称定义简单的排序表达式。您可以连接表达式以将多个条件收集到一个表达式中。
- 第一种
Sort sort = Sort.by("firstname").ascending()
.and(Sort.by("lastname").descending());
- 要以更类型安全的方式定义排序表达式,请从要定义排序表达式的类型开始,并使用方法引用来定义要排序的属性。
Sort.TypedSort<SysUser> sort = Sort.sort(SysUser.class);
Sort and = sort.by(SysUser::getId).ascending()
.and(sort.by(SysUser::getName).descending());
注意 TypedSort.by(…)
(通常)使用 CGlib 来使用运行时代理,这在使用 Graal VM Native 等工具时可能会干扰本机映像编译。
- 如果你在项目中使用到了Querydsl框架,可使用元模型来定义
QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));
限制查询结果
first
您可以使用或关键字来限制查询方法的结果top
,这两个关键字可以互换使用。您可以附加一个可选的数值top
或first
指定要返回的最大结果大小。如果省略该数字,则假定结果大小为 1。以下示例显示如何限制查询大小:
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
限制表达式还支持Distinct
支持不同查询的数据存储的关键字。Optional
另外,对于将结果集限制为一个实例的查询,支持使用关键字将结果包装起来。
如果将分页或切片应用于限制查询分页(以及可用页面数量的计算),则它将应用于有限结果内。
通过使用参数与动态排序相结合来限制结果,Sort
您可以表达“K”个最小元素和“K”个最大元素的查询方法。
返回集合或可迭代对象的方法
返回多个结果的查询方法可以使用标准 Java Iterable
、List
和Set
. 除此之外,我们支持返回 Spring Data 的Streamable
、 的自定义扩展Iterable
以及Vavr提供的集合类型。请参阅解释所有可能的查询方法返回类型的附录。
使用 Streamable 作为查询方法返回类型
您可以用作任何集合类型Streamable
的替代。Iterable
它提供了访问非并行Stream
(缺少)的便捷方法以及直接遍历元素并将 与其他元素连接的Iterable
能力:….filter(…)``….map(…)``Streamable
例如下面:
interface PersonRepository extends Repository<Person, Long> {
Streamable<Person> findByFirstnameContaining(String firstname);
Streamable<Person> findByLastnameContaining(String lastname);
}
Streamable<Person> result = repository.findByFirstnameContaining("av")
.and(repository.findByLastnameContaining("ea"));
注意 Streamable中的and方法表示拼接的意思,并不是两个查询出来的相交,全部查询出来放在一起。
在spring官网中还有两种自定义的集合迭代类型,这里就不介绍了,上面这一种也已经够用了
方法的空处理
从 Spring Data 2.0 开始,返回单个聚合实例的存储库 CRUD 方法使用 Java 8Optional
来指示可能缺少值。除此之外,Spring Data 支持在查询方法上返回以下包装类型:
com.google.common.base.Optional
scala.Option
io.vavr.control.Option
或者,查询方法可以选择根本不使用包装类型。然后通过返回 来指示不存在查询结果null
。返回集合、集合替代项、包装器和流的存储库方法保证永远不会返回,null
而是返回相应的空表示。有关详细信息,请参阅“存储库查询返回类型”。
流式查询结果
Stream<T>
您可以使用 Java 8作为返回类型来增量处理查询方法的结果。不是将查询结果包装在Stream
,而是使用数据存储特定的方法来执行流处理。
Stream<SysUser> findByNameLike(String name);
Stream
可能会包装底层数据存储特定的资源,因此必须在使用后关闭。您可以Stream
使用close()
方法或使用 Java 7try-with-resources
块手动关闭,如下例所示:
前并非所有 Spring Data 模块都支持
Stream<T>
返回类型。spring官网提示
异步查询结果
您可以使用Spring 的异步方法运行功能来异步运行存储库查询。这意味着该方法在调用后立即返回,而实际查询发生在已提交给 Spring 的任务中TaskExecutor
。异步查询与反应式查询不同,不应混合使用。有关反应式支持的更多详细信息,请参阅特定的文档。以下示例显示了许多异步查询:
@Async
Future<User> findByFirstname(String firstname);
@Async
CompletableFuture<User> findOneByFirstname(String firstname);
@Async
ListenableFuture<User> findOneByLastname(String lastname);
上面这几个方法的返回类型,都是用于异步返回接收,属于异步编程工具
自定义查询
要实现自定义查询,也首先需要去对应的dao层接口下定义方法,这个方法也就不需要遵循什么派生规则,但是需要使用到注解指名sql。
使用@Query
使用命名查询来声明实体的查询是一种有效的方法,并且对于少量查询来说效果很好。由于查询本身与运行它们的 Java 方法相关联,因此您实际上可以使用 Spring Data JPA 注释直接绑定它们,@Query
而不是将它们注释到域类。这将域类从持久性特定信息中解放出来,并将查询共同定位到存储库接口。
注释到查询方法的查询优先于使用定义的查询@NamedQuery
或在 中声明的命名查询orm.xml
。
以下示例显示了使用@Query
注释创建的查询:
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from SysUser s where s.name=?1")
List<SysUser> getByName(String name);
}
注意: 这里的?+数字
代表一个占位符,比较特殊的是数字从1开始,而非从0开始。?1代表顺序中的第一个参数。
使用命名参数绑定查询名称的方法
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from SysUser s where s.name=:name")
List<SysUser> getByName(@Param("name") String name);
}
注意: =:
加上变量名的方式来绑定参数,这里是与方法参数中有@Param的值匹配的,而不是与实际参数匹配的,且@Param的顺序可以随意放置。
利用@Query进行修改
当使用@Query时,如果想要修改,需要加入@Modifying进行修饰,相当于 @Update 注解。
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Transactional
@Modifying
@Query(value = "update SysUser s set s.price=?1 where s.name=?2")
Integer update(Float price,String name);
}
这样做会触发注释到该方法的查询作为更新查询而不是选择查询。由于EntityManager
在执行修改查询后 可能包含过时的实体,因此我们不会自动清除它(有关详细信息,请参阅的JavaDocEntityManager.clear()
),因为这实际上会删除EntityManager
. 如果您希望EntityManager
自动清除,可以将@Modifying
注释的clearAutomatically
属性设置为true
。其实在高版本中,可以不管了,加不加无所谓
注释@Modifying
仅与注释结合使用才相关@Query
。派生查询方法或自定义方法不需要此注释。
注意: 再进行insert , update , delete
操作时,必须配置事务。
原生sql查询
nativeQuery=true时,指可以用原生的sql运行。例如: 所谓本地查询,就是使用原生的sql语句(根据数据库的不同,在sql的语法或结构方面可能有所区别)进行查询数据库的操作。
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Transactional
@Modifying
@Query(value = "update sys_user s set s.price=?1 where s.user_name=?2",nativeQuery = true)
Integer update(Float price,String name);
}
如果没有这个属性,那就是用springdata jpa的对象来进行查询。
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Transactional
@Modifying
@Query(value = "update SysUser s set s.price=?1 where s.name=?2")
Integer update(Float price,String name);
}
与分页和排序一起使用
PageRequest
排序可以通过提供或直接使用来完成Sort
。Order
实例中实际使用的属性Sort
需要匹配您的域模型,这意味着它们需要解析为查询中使用的属性或别名。JPQL 将其定义为状态字段路径表达式。
使用任何不可引用的路径表达式都会导致Exception
.
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from SysUser s where s.name=?1")
Page<SysUser> select(String name, Pageable pageable);
}
Sort
但是,与 with 一起使用@Query
可以让您潜入子句中包含函数的非路径检查Order
实例ORDER BY
。这是可能的,因为Order
附加到给定的查询字符串。默认情况下,Spring Data JPA 拒绝任何Order
包含函数调用的实例,但您可以使用它JpaSort.unsafe
来添加可能不安全的排序。
以下示例使用Sort
和JpaSort
,包括 上的不安全选项JpaSort
:
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.lastname like ?1%")
List<User> findByAndSort(String lastname, Sort sort);
@Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}
repo.findByAndSort("lannister", Sort.by("firstname")); //`Sort`指向域模型中属性的有效表达式
repo.findByAndSort("stark", Sort.by("LENGTH(firstname)")); //`ort`包含函数调用无效。抛出异常。
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); //有效`Sort`包含明确*不安全的* `Order`
repo.findByAsArrayAndSort("bolton", Sort.by("fn_len")); //`Sort`指向别名函数的有效表达式。
使用SpEl表达式
从 Spring Data JPA 版本 1.4 开始,我们支持在使用@Query
. 运行查询时,将根据一组预定义的变量来评估这些表达式。Spring Data JPA 支持一个名为 的变量entityName
。它的用法是select x from #{#entityName} x
. 它插入entityName
与给定存储库关联的域类型。解决方法entityName
如下:如果域类型在@Entity
注释上设置了 name 属性,则使用它。否则,将使用域类型的简单类名。
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from #{#entityName} s where s.name=?1")
Page<SysUser> select(String name, Pageable pageable);
}
为了避免在注释的查询字符串中声明实际的实体名称@Query
,您可以使用该#{#entityName}
变量。
可以entityName
使用@Entity
注释进行自定义。orm.xml
SpEL 表达式不支持 自定义
用于操作参数的 SpEL 表达式也可用于操作方法参数。在这些 SpEL 表达式中,实体名称不可用,但参数可用。可以通过名称或索引来访问它们,如以下示例所示。
通过索引访问
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from #{#entityName} s where s.name=?#{escape([0])}")
Page<SysUser> select(String name, Pageable pageable);
}
escape: 专门用来检索特殊字符的。
通过名称访问也就是命名查询
清理输入值
@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}")
List<User> findContainingEscaped(String namePart);
鉴于存储库接口中的此方法声明findContainingEscaped("Peter_")
会找到Peter_Parker
,但不会找到Peter Parker
。可以通过设置注释escapeCharacter
的来配置使用的转义字符@EnableJpaRepositories
。请注意,SpEL 上下文中可用的方法escape(String)
只会转义 SQL 和 JPQL 标准通配符_
和%
。如果底层数据库或 JPA 实现支持其他通配符,这些通配符将不会被转义。
动态类型
希望选择在调用时使用的类型(这使其成为动态的)。要应用动态投影,请使用如下例所示的查询方法:
@Repository
public interface SysUserDao extends JpaRepository<SysUser,Integer>{
@Query(value = "select s from #{#entityName} s where s.name=?1")
<T> Page<T> select(String name, Pageable pageable,Class<T> Class);
}
@Test
public void Test1(){
Page<SysUser> select = sysUserDao.select("张杰 1", PageRequest.of(1, 3), SysUser.class);
select.forEach(System.out::println);
}
检查类型的查询参数Class
是否有资格作为动态投影参数。如果查询的实际返回类型等于参数的通用参数类型Class
,则匹配的Class
参数不可再查询或 SpEL 表达式中使用。如果您想使用Class
参数作为查询参数,请确保使用不同的通用参数,例如Class<?>
。
QueryDsl框架(推荐与jpa一起)
QueryDsl作用
-
Querydsl支持代码自动完成,因为是纯Java API编写查询,因此主流Java IDE对其的代码自动完成功能支持几乎可以发挥到极致(因为是纯Java代码,所以支持很好)
-
Querydsl几乎可以避免所有的SQL语法错误(当然用错了Querydsl API除外,因为不写SQL了,因此想用错也难)
-
Querydsl采用Domain类型的对象和属性来构建查询,因此查询绝对是类型安全的,不会因为条件类型而出现问题
-
Querydsl采用纯Java API的作为SQL构建的实现可以让代码重构发挥到另一个高度
-
Querydsl的另一个优势就是可以更轻松的进行增量查询的定义
使用
在Spring环境下,可以通过两种风格来使用QueryDSL。
一种是使用JPAQueryFactory的原生QueryDSL风格, 另一种是基于Spring Data提供的QueryDslPredicateExecutor<T>的Spring-data风格。
使用QueryDslPredicateExecutor<T>可以简化一些代码,使得查询更加优雅。 而JPAQueryFactory的优势则体现在其功能的强大,支持更复杂的查询业务。甚至可以用来进行更新和删除操作。
依赖和插件
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
在高版本中 spring家族帮我们整理了版本,不要指定版本,出现问题再去修改版本号
<build>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-resources</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>
添加这个插件是为了让程序自动生成query type(查询实体,命名方式为:“Q”+对应实体名)。
上文引入的依赖中querydsl-apt即是为此插件服务的。
注:在使用过程中,如果遇到query type无法自动生成的情况,用maven更新一下项目即可解决(右键项目->Maven->Update 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>
生成Q类的实体类
完成之后就会在target下生成对应的Q类
JPAQueryFactory风格
QueryDSL 在支持JPA的同时,也提供了对 Hibernate 的支持。可以通过 HibernateQueryFactory
来使用。
装配注入
EntityManager是完成持久化操作的核心对象,需要在JPAQueryFactory工厂中配置
- 使用配置类
@Configuration
public class QueryDslConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager){
return new JPAQueryFactory(entityManager);
}
}
单表查询操作
@SpringBootTest
public class MainTest {
@Autowired
private JPAQueryFactory jpaQueryFactory;
private QSysUser sysUser = QSysUser.sysUser;
@Test
public void Test1(){
// 查询全部
List<SysUser> fetch = jpaQueryFactory.select(sysUser)
.from(sysUser)
.fetch();
List<SysUser> fetch1 = jpaQueryFactory.selectFrom(sysUser)
.fetch();
// 查询某一个字段
List<String> fetch2 = jpaQueryFactory.select(sysUser.name)
.from(sysUser)
.fetch();
// 去重查询
List<String> fetch3 = jpaQueryFactory.selectDistinct(sysUser.name)
.from(sysUser)
.fetch();
// 获取首个查询结果
SysUser sysUser1 = jpaQueryFactory.selectFrom(sysUser)
.fetchFirst();
// 获取唯一的查询结果
// 当fetchOne查询出来是一个多个数据集合,将会抛出 ‘com.querydsl.core.NonUniqueResultException’ 错误
SysUser sysUser2 = jpaQueryFactory.select(sysUser)
.from(sysUser)
.where(sysUser.name.eq("张杰 2"))
.fetchOne();
}
}
where条件查询
List<SysUser> fetch = jpaQueryFactory.selectFrom(sysUser)
.where(sysUser.name.eq("张杰 2")
.and(sysUser.author.like("%你好 0%"))
.and(sysUser.price.between(2.0f,4.4f)))
.where(sysUser.createTime.isNull())
.fetch();
fetch.forEach(System.out::println);
注意:and连接是在where中的,如果你的where中判断条件太多,你可以在外部再进行where的拼接
BooleanBuilder条件管理(动态语句)
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(sysUser.name.eq("张杰 2"));
booleanBuilder.and(sysUser.author.like("%你好 0%"));
booleanBuilder.and(sysUser.price.between(1.3F,3.3F));
List<SysUser> fetch = jpaQueryFactory.selectFrom(sysUser).where(booleanBuilder).fetch();
fetch.forEach(System.out::println);
这种方式我感觉最方便,并且最大优点就是可以实现动态的。
@Test
public void Test1(){
// 1. 注意,and连接是在where中
sel("张杰 1","你好",1.2f,3.3f);
}
public void sel(String name,String author,Float left,Float right){
BooleanBuilder booleanBuilder = new BooleanBuilder();
if (StringUtils.hasText(name)){
booleanBuilder.and(sysUser.name.eq(name));
}
if (StringUtils.hasText(author)){
booleanBuilder.and(sysUser.author.like("%"+author+"%"));
}
if (left<right && left!=0 && right !=0){
booleanBuilder.and(sysUser.price.between(left,right));
}
List<SysUser> fetch = jpaQueryFactory.selectFrom(sysUser).where(booleanBuilder).fetch();
fetch.forEach(System.out::println);
}
这种写法的确很爽!,但是sql语句就会慢慢生疏了,所以在余下时间就需要再去练习sql
复杂的查询关系
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(sysUser.name.like("%张杰%"));
BooleanBuilder booleanBuilder1 = new BooleanBuilder();
booleanBuilder1.and(sysUser.price.between(2.3F,6.6F));
// 在第一个条件管理中在添加第二个管理条件
booleanBuilder.and(booleanBuilder1);
List<SysUser> fetch = jpaQueryFactory.selectFrom(sysUser).where(booleanBuilder).fetch();
System.out.println(fetch.size());
fetch.forEach(System.out::println);
转载自:https://juejin.cn/post/7258445326913880125