不可否认的是 JPA 使用是非常方便的,极简化的配置,只需要使用注解,无需任何 xml 的配置文件,语义简单易懂,但是,以上的一切都建立在单表查询的前提下的,我们可以使用 JPA 默认提供的方法,简单加轻松的完成 CRUD 操作。
但是如果涉及到多表动态查询, JPA 的功能就显得有些捉襟见肘了,虽然我们可以使用注解 @Query
,在这个注解中写 SQL 或者 HQL 都是在拼接字符串,并且拼接后的字符串可读性非常的差,当然 JPA 还为我们提供了 Specification
来做这件事情,从我个人使用体验上来讲,可读性虽然还不错,但是在初学者上手的时候, Predicate
和 CriteriaBuilder
使用方式估计能劝退不少人,而且如果直接执行 SQL 连表查询,获得是一个 Object[]
,类型是什么?字段名是什么?这些都无法直观的获得,还需我们手动将 Object[]
映射到我们需要的 Model
类里面去,这种使用体验无疑是极其糟糕的。
这一切都在 QueryDSL 出世以后终结了, QueryDSL 语法与 SQL 非常相似,代码可读性非常强,异常简介优美,,并且与 JPA 高度集成,无需多余的配置,从笔者个人使用体验上来讲是非常棒的。可以这么说,只要会写 SQL ,基本上只需要看一下示例代码完全可以达到入门的级别。
QueryDSL 简介:
QueryDSL 是一个非常活跃的开源项目,目前在 Github 上的发布的 Release 版本已经多达 251 个版本,目前最新版是 4.2.1 ,并且由 Querydsl Google组 和 StackOverflow 两个团队提供支持。
QueryDSL 是一个框架,可用于构造静态类型的类似SQL的查询。可以通过诸如 QueryDSL 之类的 API 构造查询,而不是将查询编写为内联字符串或将其外部化为XML文件。
引入maven依赖和插件
SpringBoot 内部已规定了版本
<dependency>
<groupId>com.querydslgroupId>
<artifactId>querydsl-aptartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>com.querydslgroupId>
<artifactId>querydsl-jpaartifactId>
dependency>
<plugin>
<groupId>com.mysema.mavengroupId>
<artifactId>apt-maven-pluginartifactId>
<version>1.1.3version>
<executions>
<execution>
<goals>
<goal>processgoal>
goals>
<configuration>
<outputDirectory>target/generated-sources/javaoutputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessorprocessor>
configuration>
execution>
executions>
plugin>
运行 mvn compile, 将生成Query实体(target包内),以Q+实体类名为名称。
1 创建BaseService,后其他service都继承BaseService 通过 JPAQueryFactory进行增删改查操作
public class BaseService {
@Autowired
protected EntityManager entityManager;
protected JPAQueryFactory jpaQueryFactory;
@PostConstruct
public void init(){
jpaQueryFactory = new JPAQueryFactory(entityManager);
}
}
@Test
public void QDSL1(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
//delete from mt_product_info wher id =1 or name = '删除'
BooleanExpression eq = mtProductInfo.id.eq(1);
BooleanExpression eq2 = eq.or( mtProductInfo.name.eq("删除" ));
jpaQueryFactory.delete(mtProductInfo).where(eq2).execute();
}
@Test
@Transactional
public void QDSL_update(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
//update mt_product_info set name = "删除" where id =2 or name ='张三'
jpaQueryFactory.update(mtProductInfo)
.set(mtProductInfo.name,"删除")
.where(
mtProductInfo.id.eq(2)
.and(mtProductInfo.name.eq("张三"))
);
}
@Test
public void QDSL_query1(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
//返回的类型 和 查询的字段
QBean<MtProductInfo> title = Projections.bean(MtProductInfo.class, mtProductInfo.id, mtProductInfo.summarize.as("name"));
List<MtProductInfo> fetch = jpaQueryFactory.select(title)
.from(mtProductInfo) //要查询的表
.where(mtProductInfo.id.ne(1))//查询条件
.fetch();//开始查询
fetch.forEach(System.out::println);
}
多表联查返回Map
@Test
public void QDSL_query2(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
QMtComFile mtComFile = QMtComFile.mtComFile;
//创建查询内容和查询条件
List<Tuple> fetch = jpaQueryFactory.select(mtProductInfo.id, mtProductInfo.name, mtComFile.filename)
.from(mtProductInfo, mtComFile)
.where(mtProductInfo.indexIcon.eq(mtComFile.id)) //2表联查关系
.fetch();
//处理查询结果 封装为map
List<Map> collect = fetch.stream().map(o -> {
Map map = new HashMap();
map.put("id", o.get(mtProductInfo.id));
map.put("name", o.get(mtProductInfo.name));
map.put("filename", o.get(mtComFile.filename));
return map;
}).collect(Collectors.toList());
System.out.println(collect);
}
多表联查返回对象
@Test
public void QDSL_query2(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
QMtComFile mtComFile = QMtComFile.mtComFile;
//将结果映射为 MtProductInfo 通过 字段名映射
QBean<MtProductInfo> bean = Projections.bean(MtProductInfo.class, mtProductInfo.id, mtProductInfo.name, mtComFile.filename);
//创建查询内容和查询条件
List<MtProductInfo> fetch = jpaQueryFactory.select(bean)
.from(mtProductInfo, mtComFile)
.where(mtProductInfo.indexIcon.eq(mtComFile.id)) //2表联查关系
.fetch();
System.out.println(fetch);
}
@Test
public void QDSL_query2(){
QMtProductInfo mtProductInfo = QMtProductInfo.mtProductInfo;
QMtComFile mtComFile = QMtComFile.mtComFile;
//将结果映射为 MtProductInfo 通过 字段名映射
QBean<MtProductInfo> bean = Projections.bean(MtProductInfo.class, mtProductInfo.id, mtProductInfo.name, mtComFile.filename);
//创建分页
PageRequest pageRequest = PageRequest.of(10, 5);
//创建查询内容和查询条件
List<MtProductInfo> fetch = jpaQueryFactory.select(bean)
.from(mtProductInfo, mtComFile)
.where(mtProductInfo.indexIcon.eq(mtComFile.id)) //2表联查关系
.orderBy(mtProductInfo.name.asc(),mtProductInfo.status.desc()) //排序
.offset(pageRequest.getOffset()) //获取 offset
.limit(pageRequest.getPageSize())
//获取 pageSize
.fetch();
fetch.forEach(System.out::println);
}