前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springJPA 之 QueryDSL(一)

springJPA 之 QueryDSL(一)

作者头像
全栈程序员站长
发布2022-09-20 16:05:21
3.9K0
发布2022-09-20 16:05:21
举报
文章被收录于专栏:全栈程序员必看

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

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/167910.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档