springJPA 之 QueryDSL(一)

springJPA 之 QueryDSL(一)引言 不可否认的是 JPA 使用是非常方便的 极简化的配置 只需要使用注解 无需任何 xml 的配置文件 语义简单易懂 但是 以上的一切都建立在单表查询的前提下的 我们可以使用 JPA 默认提供的方法 简单加轻松的完成 CRUD 操作 但是如果涉及到多表动态查询 JPA 的功能就显得有些捉襟见肘了 虽然我们可以使用注解 Query 在这个注解中写 SQL 或者 HQL

引言
不可否认的是 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文件。

例如,与简单字符串相比,使用 API 的好处是

IDE中的代码完成

几乎没有语法无效的查询

可以安全地引用域类型和属性

更好地重构域类型的更改

QueryDSL 使用实战
3.1 引入 Maven 依赖
代码清单:spring-boot-jpa-querydsl/pom.xml
com.querydsl querydsl-apt provided com.querydsl querydsl-jpa COPY 这里无需指定版本号,已在 spring-boot-dependencies 工程中定义。 3.2 添加 Maven 插件 添加这个插件是为了让程序自动生成 query type (查询实体,命名方式为:”Q”+对应实体名)。 上文引入的依赖中 querydsl-apt 即是为此插件服务的。

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

代码清单:spring-boot-jpa-querydsl/pom.xml

org.springframework.boot spring-boot-maven-plugin com.mysema.maven apt-maven-plugin 1.1.3 process target/generated-sources/java com.querydsl.apt.jpa.JPAAnnotationProcessor COPY 3.3 更新和删除 在 JPA 中已经为我们提供了非常简便的更新和删除的使用方式,我们完全没有必要使用 QueryDSL 的更新和删除,不过这里还是给出用法,供大家参考:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

@Override
public Long update(String id, String nickName) {
QUserModel userModel = QUserModel.userModel;
// 更新
return queryFactory.update(userModel).set(userModel.nickName, nickName).where(userModel.id.eq(id)).execute();
}

@Override
public Long delete(String id) {
QUserModel userModel = QUserModel.userModel;
// 删除
return queryFactory.delete(userModel).where(userModel.id.eq(id)).execute();
}COPY
3.2 查询
QueryDSL 在查询这方面可以说玩的非常花了,比如一些有关 select() 和 fetch() 常用的写法如下:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

@Override
public List selectAllNameList() {
QUserModel userModel = QUserModel.userModel;
// 查询字段
return queryFactory.select(userModel.nickName).from(userModel).fetch();
}

@Override
public List selectAllUserModelList() {
QUserModel userModel = QUserModel.userModel;
// 查询实体
return queryFactory.selectFrom(userModel).fetch();
}

@Override
public List selectAllUserDTOList() {
QUserModel userModel = QUserModel.userModel;
QLessonModel lessonModel = QLessonModel.lessonModel;
// 连表查询实体并将结果封装至DTO
return queryFactory
.select(
Projections.bean(UserDTO.class, userModel.nickName, userModel.age, lessonModel.startDate, lessonModel.address, lessonModel.name)
)
.from(userModel)
.leftJoin(lessonModel)
.on(userModel.id.eq(lessonModel.userId))
.fetch();
}

/** * 根据QueryDSL查询 * @return */ @RequestMapping(value = "/selectWithQueryDSL") public List<GoodDTO> selectWithQueryDSL() { //商品基本信息 QGoodInfoBean _Q_good = QGoodInfoBean.goodInfoBean; //商品类型 QGoodTypeBean _Q_good_type = QGoodTypeBean.goodTypeBean; return queryFactory .select( Projections.bean( GoodDTO.class,//返回自定义实体的类型 _Q_good.id, _Q_good.price, _Q_good.title, _Q_good.unit, _Q_good_type.name.as("typeName"),//使用别名对应dto内的typeName _Q_good_type.id.as("typeId")//使用别名对应dto内的typeId ) ) .from(_Q_good,_Q_good_type)//构建两表笛卡尔集 .where(_Q_good.typeId.eq(_Q_good_type.id))//关联两表 .orderBy(_Q_good.order.desc())//倒序 .fetch(); }

@Override
public List selectDistinctNameList() {
QUserModel userModel = QUserModel.userModel;
// 去重查询
return queryFactory.selectDistinct(userModel.nickName).from(userModel).fetch();
}

@Override
public UserModel selectFirstUser() {
QUserModel userModel = QUserModel.userModel;
// 查询首个实体
return queryFactory.selectFrom(userModel).fetchFirst();
}

@Override
public UserModel selectUser(String id) {
QUserModel userModel = QUserModel.userModel;
// 查询单个实体,如果结果有多个,会抛NonUniqueResultException。
return queryFactory.selectFrom(userModel).fetchOne();
}COPY
3.4 复杂查询操作
上面列举了简单的查询,但实际我们会遇到相当复杂的操作,比如子查询,多条件查询,多表连查,使用示例如下:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/LessonServiceImpl.java

@Service
public class LessonServiceImpl implements LessonService {

@Autowired
JPAQueryFactory queryFactory;

@Override
public List findLessonList(String name, Date startDate, String address, String userId) throws ParseException {
QLessonModel lessonModel = QLessonModel.lessonModel;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”);
// 多条件查询示例
return queryFactory.selectFrom(lessonModel)
.where(
lessonModel.name.like(“%” + name + “%”)
.and(lessonModel.address.contains(address))
.and(lessonModel.userId.eq(userId))
.and(lessonModel.startDate.between(simpleDateFormat.parse(“2018-12-31 00:00:00”), new Date()))
)
.fetch();
}

@Override
public List findLessonDynaList(String name, Date startDate, String address, String userId) throws ParseException {
QLessonModel lessonModel = QLessonModel.lessonModel;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”);

// 动态查询示例
BooleanBuilder builder = new BooleanBuilder();

if (!StringUtils.isEmpty(name)){
builder.and(lessonModel.name.like(“%” + name + “%”));
}

if (startDate != null) {
builder.and(lessonModel.startDate.between(simpleDateFormat.parse(“2018-12-31 00:00:00”), new Date()));
}

if (!StringUtils.isEmpty(address)) {
builder.and(lessonModel.address.contains(address));
}

if (!StringUtils.isEmpty(userId)) {
builder.and(lessonModel.userId.eq(userId));
}

return queryFactory.selectFrom(lessonModel).where(builder).fetch();
}

@Override
public List findLessonSubqueryList(String name, String address) {
QLessonModel lessonModel = QLessonModel.lessonModel;
// 子查询示例,并无实际意义
return queryFactory.selectFrom(lessonModel)
.where(lessonModel.name.in(
JPAExpressions
.select(lessonModel.name)
.from(lessonModel)
.where(lessonModel.address.eq(address))
))
.fetch();
}
}COPY
3.5 Mysql 聚合函数
QueryDSL 已经内置了一些常用的 Mysql 的聚合函数,如果遇到 QueryDSL 没有提供的聚合函数也无需慌张, QueryDSL 为我们提供了 Expressions 这个类,我们可以使用这个类手动拼接一个就好,如下示例:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

@Override
public String mysqlFuncDemo(String id, String nickName, int age) {

QUserModel userModel = QUserModel.userModel;

// Mysql 聚合函数示例

// 聚合函数-avg()
Double averageAge = queryFactory.select(userModel.age.avg()).from(userModel).fetchOne();

// 聚合函数-sum()
Integer sumAge = queryFactory.select(userModel.age.sum()).from(userModel).fetchOne();

// 聚合函数-concat()
String concat = queryFactory.select(userModel.nickName.concat(nickName)).from(userModel).fetchOne();

// 聚合函数-contains()
Boolean contains = queryFactory.select(userModel.nickName.contains(nickName)).from(userModel).where(userModel.id.eq(id)).fetchOne();

// 聚合函数-DATE_FORMAT()
String date = queryFactory.select(Expressions.stringTemplate(“DATE_FORMAT({0},’%Y-%m-%d’)”, userModel.createDate)).from(userModel).fetchOne();

return null;

}COPY
4. 小结
有关 QueryDSL 的介绍到这里就结束了,不知道各位读者看了上面的示例,有没有一种直接读 SQL 的感觉,而且这种 SQL 还是使用 OOM 的思想,将原本 Hibernate 没有做好的事情给出了一个相当完美的解决方案,上手简单易操作,而又无需写 SQL ,实际上我们操作的还是对象类。

转载至 cnds 15553750802

编程小号
上一篇 2025-01-28 19:40
下一篇 2025-02-11 22:46

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/109860.html