使用JPA2.0条件API

本贴最后更新于 3207 天前,其中的信息可能已经斗转星移

Pro JPA2 第九章(条件 API)-干货
嗯,演示一个使用条件 API 的例子,代码是公司的,所以实体的具体内容就不展示了- -!抱歉.
实体类:
enter image description here
使用 hibernate-jpamodelgen 生成的实体元模型
enter image description here
AssembleEntity:

/** * 描述: TODO: * 包名: spring.data.specification.entity. * 作者: barton. * 日期: 16-7-11. * 项目名称: spring-data * 版本: 1.0 * JDK: since 1.8 */ public class AssembleEntity { private String username; private String oname; private String userId; private String truename; private SysUser.UserStatus status; private String rname; private Long rid; public AssembleEntity(String username) { this.username = username; } public AssembleEntity(String username, String oname, String userId, String truename, SysUser.UserStatus status, String rname, Long rid) { this.username = username; this.oname = oname; this.userId = userId; this.truename = truename; this.status = status; this.rname = rname; this.rid = rid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getOname() { return oname; } public void setOname(String oname) { this.oname = oname; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getTruename() { return truename; } public void setTruename(String truename) { this.truename = truename; } public SysUser.UserStatus getStatus() { return status; } public void setStatus(SysUser.UserStatus status) { this.status = status; } public String getRname() { return rname; } public void setRname(String rname) { this.rname = rname; } public Long getRid() { return rid; } public void setRid(Long rid) { this.rid = rid; } @Override public String toString() { return "AssembleEntity{" + "username='" + username + '\'' + ", oname='" + oname + '\'' + ", userId='" + userId + '\'' + ", truename='" + truename + '\'' + ", status='" + status + '\'' + ", rname='" + rname + '\'' + ", rid='" + rid + '\'' + '}'; } }

AccountService:

import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import spring.data.specification.entity.*; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; /** * 描述: TODO: * 包名: spring.data.specification.service. * 作者: barton. * 日期: 16-7-2. * 项目名称: spring-data * 版本: 1.0 * JDK: since 1.8 */ @Service public class AccountService { @PersistenceContext private EntityManager em; /** * 演示如何返回List<Tuple> 类型的数据. * Tuple是一个元组,表示可以通过Object[]数组的形式访问,也可以通过TupleElement的别名进行访问. */ public List<Tuple> selectAccountByPositionAndStatus(String orgName, String status, String regionName, PageRequest page, String searchParam) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Tuple> cq = cb.createTupleQuery(); // 根(主表) Root<SysUser> user = cq.from(SysUser.class); // 设置表连接关系 Join<SysUser, SysOrganization> organization = user.join(SysUser_.sysOrganization, JoinType.LEFT); Join<SysUser, SalesMan> salesMan = user.join(SysUser_.salseMan, JoinType.LEFT); Join<SalesMan, Region> region = salesMan.join(SalesMan_.region, JoinType.LEFT); Join<SysUser, SysRole> role = user.join(SysUser_.sysRoles, JoinType.LEFT); // 这种方式用在CriteriaQuery<Tuple> 是不可以的. // List<Selection> selections = new ArrayList<>(); // // selections.add(user.get(SysUser_.username).alias("username")); // selections.add(user.get(SysUser_.sysOrganization).get(SysOrganization_.name).alias("name")); // 设置要检索的字段 cq.multiselect(user.get(SysUser_.username).alias("username"), organization.get(SysOrganization_.name).alias("oname"), user.get(SysUser_.id).alias("userId"), salesMan.get(SalesMan_.truename).alias("truename"), user.get(SysUser_.status).alias("status"), region.get(Region_.name).alias("rname"), role.get(SysRole_.id).alias("rid") ); // 设置检索条件 List<Predicate> conditions = getConditions(orgName, cb, organization, user, status, searchParam, salesMan); cq.where(conditions.toArray(new Predicate[conditions.size()])); TypedQuery<Tuple> query = em.createQuery(cq); return query.getResultList(); } /** * 演示如何返回List<Object[]> 类型的数据. * 虽然可以在mulselect方法中设置别名,但无法使用. */ public List<Object[]> selectAccountByPositionAndStatus2(String orgName, String status, String regionName, PageRequest page, String searchParam) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class); // 根(主表) Root<SysUser> user = cq.from(SysUser.class); // 设置表连接关系 Join<SysUser, SysOrganization> organization = user.join(SysUser_.sysOrganization, JoinType.LEFT); Join<SysUser, SalesMan> salesMan = user.join(SysUser_.salseMan, JoinType.LEFT); Join<SalesMan, Region> region = salesMan.join(SalesMan_.region, JoinType.LEFT); Join<SysUser, SysRole> role = user.join(SysUser_.sysRoles, JoinType.LEFT); // subquery 不支持出现在select语句中 // // 子查询 // Subquery<SysUser> sq = cq.subquery(SysUser.class); // Root<SalesMan> sqSm = sq.from(SalesMan.class); // Join<SalesMan, SysUser> sqSmJoin = sqSm.join(SalesMan_.user, JoinType.LEFT); // sq.select(sqSmJoin).where(cb.equal(salesMan.get(SalesMan_.id), "A37018206110")); // 设置要检索的字段 cq.multiselect(user.get(SysUser_.username).alias("username"), organization.get(SysOrganization_.name).alias("oname"), // 链式编程 user.get(SysUser_.id).alias("userId"), salesMan.get(SalesMan_.truename).alias("truename"), user.get(SysUser_.status).alias("status"), region.get(Region_.name).alias("rname"), role.get(SysRole_.id).alias("rid")/*, sq*/ ); // 设置检索条件 List<Predicate> conditions = getConditions(orgName, cb, organization, user, status, searchParam, salesMan); cq.where(conditions.toArray(new Predicate[conditions.size()])); TypedQuery<Object[]> query = em.createQuery(cq); return query.getResultList(); } /** * 演示如何返回List<AssembleEntity> 类型的数据. * 将select方法中设置的字段 直接映射到一个实体类中 * where条件中 使用子查询 * 虽然可以在mulselect方法中设置别名,但无法使用. * 注意点: * 1.AssembleEntity中必须有和select方法中所这是要检索的字段顺序,类型一致的构造方法 * 2.跟别名无关 */ public List<AssembleEntity> selectAccountByPositionAndStatus3(String orgName, String status, String regionName, PageRequest page, String searchParam) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<AssembleEntity> cq = cb.createQuery(AssembleEntity.class); // 根(主表) Root<SysUser> user = cq.from(SysUser.class); // // 设置表连接关系 Join<SysUser, SysOrganization> organization = user.join(SysUser_.sysOrganization, JoinType.LEFT); Join<SysUser, SalesMan> salesMan = user.join(SysUser_.salseMan, JoinType.LEFT); Join<SalesMan, Region> region = salesMan.join(SalesMan_.region, JoinType.LEFT); Join<SysUser, SysRole> role = user.join(SysUser_.sysRoles, JoinType.LEFT); // 设置要检索的字段 cq.select(cb.construct(AssembleEntity.class, user.get("username"), organization.get(SysOrganization_.name), user.get(SysUser_.id), salesMan.get(SalesMan_.truename), user.get(SysUser_.status), region.get(Region_.name), role.get(SysRole_.id))); // 设置检索条件 List<Predicate> conditions = getConditions(orgName, cb, organization, user, status, searchParam, salesMan); // 子查询 Subquery<SysUser> sq = cq.subquery(SysUser.class); Root<SalesMan> sqSm = sq.from(SalesMan.class); Join<SalesMan, SysUser> sqSmJoin = sqSm.join(SalesMan_.user, JoinType.LEFT); sq.select(sqSmJoin).where(cb.equal(salesMan.get(SalesMan_.id), "A37018206110")); //conditions.add(cb.in(user).value(sq)); conditions.add(cb.exists(sq)); cq.where(conditions.toArray(new Predicate[conditions.size()])); TypedQuery<AssembleEntity> query = em.createQuery(cq); return query.getResultList(); } /** * in的写法 */ private CriteriaBuilder.In getIn(CriteriaBuilder cb, Root<SysUser> root) { CriteriaBuilder.In<String> in = cb.in(root.get(SysUser_.status).as(String.class)); in.value("0"); in.value("1"); return in; } private List<Predicate> getConditions(String orgName, CriteriaBuilder cb, Join<SysUser, SysOrganization> organization, Root<SysUser> user, String status, String searchParam, Join<SysUser, SalesMan> salesMan) { // 设置检索条件 List<Predicate> conditions = new ArrayList<>(); if (StringUtils.isNotBlank(orgName)) { if (!StringUtils.startsWith(orgName, "al")) { Predicate p; if (StringUtils.equals("大区总监", orgName.trim())) { p = cb.and(cb.equal(organization.get(SysOrganization_.name).as(String.class), "大区总监")); } else { p = cb.and(cb.equal(organization.get(SysOrganization_.name).as(String.class), "服务站经理")); } conditions.add(p); conditions.add(getIn(cb, user)); } else { if (!StringUtils.equals("used", status)) { conditions.add(cb.and(cb.equal(user.get(SysUser_.status).as(String.class), status))); } else { conditions.add(getIn(cb, user)); } } if (StringUtils.isNotBlank(searchParam)) { conditions.add(cb.like(salesMan.get(SalesMan_.truename).as(String.class), "%" + searchParam + "%")); } } return conditions; } }

AccounServiceTest:

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.Assert; import spring.data.specification.entity.AssembleEntity; import spring.data.specification.service.AccountService; import javax.persistence.Tuple; import java.util.List; /** * 描述: TODO: * 包名: spring.data.specification. * 作者: barton. * 日期: 16-7-2. * 项目名称: spring-data * 版本: 1.0 * JDK: since 1.8 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = SpecificationApplication.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testA() { List<Tuple> result = accountService.selectAccountByPositionAndStatus("al", "used", "中国", null, null); Assert.notNull(result); result.forEach(tuple -> System.out.println(tuple.get("userId"))); } @Test public void testB() { List<Object[]> result = accountService.selectAccountByPositionAndStatus2("al", "used", "中国", null, null); Assert.notNull(result); } @Test public void testC() { List<AssembleEntity> result = accountService.selectAccountByPositionAndStatus3("al", "used", "中国", null, null); Assert.notNull(result); result.forEach(assembleEntity -> System.out.println(assembleEntity.getUsername())); } }

testA()生成的 sql:

SELECT sysuser0_.username AS col_0_0_, sysorganiz1_.name AS col_1_0_, sysuser0_.user_id AS col_2_0_, salesman2_.truename AS col_3_0_, sysuser0_.status AS col_4_0_, region3_.name AS col_5_0_, sysrole5_.role_id AS col_6_0_ FROM sys_user sysuser0_ LEFT OUTER JOIN sys_organization sysorganiz1_ ON sysuser0_.organization_id=sysorganiz1_.organization_id LEFT OUTER JOIN sys_salesman salesman2_ ON sysuser0_.user_id=salesman2_.user_id LEFT OUTER JOIN sys_region region3_ ON salesman2_.region_id=region3_.region_id LEFT OUTER JOIN sys_coordinates region3_1_ ON region3_.region_id=region3_1_.region_id LEFT OUTER JOIN sys_users_roles sysroles4_ ON sysuser0_.user_id=sysroles4_.user_id LEFT OUTER JOIN sys_role sysrole5_ ON sysroles4_.role_id =sysrole5_.role_id WHERE CAST(sysuser0_.status AS VARCHAR2(255 CHAR)) IN (? , ?);

testB()生成的 sql:

SELECT sysuser0_.username AS col_0_0_, sysorganiz1_.name AS col_1_0_, sysuser0_.user_id AS col_2_0_, salesman2_.truename AS col_3_0_, sysuser0_.status AS col_4_0_, region3_.name AS col_5_0_, sysrole5_.role_id AS col_6_0_ FROM sys_user sysuser0_ LEFT OUTER JOIN sys_organization sysorganiz1_ ON sysuser0_.organization_id=sysorganiz1_.organization_id LEFT OUTER JOIN sys_salesman salesman2_ ON sysuser0_.user_id=salesman2_.user_id LEFT OUTER JOIN sys_region region3_ ON salesman2_.region_id=region3_.region_id LEFT OUTER JOIN sys_coordinates region3_1_ ON region3_.region_id=region3_1_.region_id LEFT OUTER JOIN sys_users_roles sysroles4_ ON sysuser0_.user_id=sysroles4_.user_id LEFT OUTER JOIN sys_role sysrole5_ ON sysroles4_.role_id =sysrole5_.role_id WHERE CAST(sysuser0_.status AS VARCHAR2(255 CHAR)) IN (? , ?);

testC()称称的 sql:

SELECT sysuser0_.username AS col_0_0_, sysorganiz1_.name AS col_1_0_, sysuser0_.user_id AS col_2_0_, salesman2_.truename AS col_3_0_, sysuser0_.status AS col_4_0_, region3_.name AS col_5_0_, sysrole5_.role_id AS col_6_0_ FROM sys_user sysuser0_ LEFT OUTER JOIN sys_organization sysorganiz1_ ON sysuser0_.organization_id=sysorganiz1_.organization_id LEFT OUTER JOIN sys_salesman salesman2_ ON sysuser0_.user_id=salesman2_.user_id LEFT OUTER JOIN sys_region region3_ ON salesman2_.region_id=region3_.region_id LEFT OUTER JOIN sys_coordinates region3_1_ ON region3_.region_id=region3_1_.region_id LEFT OUTER JOIN sys_users_roles sysroles4_ ON sysuser0_.user_id=sysroles4_.user_id LEFT OUTER JOIN sys_role sysrole5_ ON sysroles4_.role_id =sysrole5_.role_id WHERE (CAST(sysuser0_.status AS VARCHAR2(255 CHAR)) IN (? , ?)) AND (EXISTS (SELECT sysuser7_.user_id FROM sys_salesman salesman6_ LEFT OUTER JOIN sys_user sysuser7_ ON salesman6_.user_id =sysuser7_.user_id WHERE salesman2_.user_id=? ));

使用 Spring-Boot-JPA 的时候的坑:

  • 使用 Specification 接口的时候,JPA2.0 规范的 multiselect()select() 方法不起作用.原因是 Spring-Boot-JPA 会自己调用一次 select() 方法,而在 JPA2.0 中,由于条件 API 对象的不可变性,同样的方法调用两次,会产生覆盖.
  • 条件 API 不支持出现在 select,from 子句的子查询

使用 Spring-Boot-JPA 时候的最佳实践(目前总结出的):

  • 最好是把上例中 AccountService 中关于条件 API 查询的按照 Spring-Boot-JPA 推荐的自定义 repository 接口的写法来写,因为这样更规范.
  • 如果 Spring-Boot-JPA 提供的功能满足不了你,或者说我在检索的时候就是只想用部分字段,还是直接用条件 API 比较好,参考上边的坑.
  • 由于实体之间的关系在执行查询的之后序列化的过程中会出现查起来没完的情况(懒加载也没用,因为序列化的时候 hibernate 代理会执行相应字段属性的 getter 方法.这些都是基于 Java 反射机制实现的,如果还不懂,最好自己去看下 Java 反射的知识),除了基本的增删改查之外,最好从 @EntityGraph,@NamedQuery,@Query,条件 API,JP QL,序列化忽略(@JsonIgnore)中选择出最合适当前情况的技术来使用.自定义 Spring 的消息转换器并不能完美的解决问题,因为在 JSON 序列化的时候,实体对象全部是 hibernate 代理对象,还是存在 session 状态的.或者是我水平还不到- -,如果有朋友能够完美解决这个问题,还请告知下,谢谢!
  • 对 JPA 实体关系管理双向关联的一些思考
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3200 引用 • 8216 回帖 • 1 关注
  • JPA
    17 引用 • 31 回帖
  • 分享

    有什么新发现就分享给大家吧!

    248 引用 • 1794 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 56 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 468 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 2 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 451 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 703 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1280 回帖 • 1 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    181 引用 • 408 回帖 • 483 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 107 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    99 引用 • 367 回帖 • 1 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    78 引用 • 396 回帖
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 690 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 29 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 60 关注
  • 安全

    安全永远都不是一个小问题。

    203 引用 • 818 回帖 • 2 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 2 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    167 引用 • 314 回帖
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    32 引用 • 108 回帖
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    56 引用 • 85 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 666 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 3 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 504 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    174 引用 • 538 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 2 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 498 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 345 关注