spring data jpa坑点 count语句加组合查询怎么处理

Spring data JPA实现动态查询的方法
Spring data JPA实现动态查询的方法
【专利摘要】本发明涉及一种Spring?data?JPA实现动态查询的方法,其中包括采用基于Spring?FactoryBean创建的动态查询确认类判断被调用的接口方法是否需要进行动态查询;采用基于Spring?FactoryBean创建的动态JPA查询类创建动态查询语句;所述的动态JPA查询类将所述的动态查询语句发送至JPA框架;所述的动态JPA查询类将JPA框架的查询结果转换成指定类型的返回对象。采用该种Spring?data?JPA实现动态查询的方法,能够实现只需要进行接口方法定义声明,而无需进行任何实际编码逻辑的实现,如果以后页面查询的需求发生变化,只需要在相应的VO类中增加或者修改属性、注解即可,大大减轻了开发人员的负担,降低了进行页面查询开发出错的可能性,增强了软件的维护性,提高了软件开发的生产力,具有更广泛的应用范围。
【专利说明】Spring data JPA实现动态查询的方法
【技术领域】
[0001]本发明涉及计算机【技术领域】,尤其涉及动态查询领域,具体是指一种Spring dataJPA实现动态查询的方法。
【背景技术】
[0002]Spring data JPA是指一种Spring数据持久化架构,是Spring (—个开源框架,为了解决企业应用程序开发复杂性而创建)基于ORM (Object/Relation Mapping,对象关系映射)框架、JPA (Java Persistence API,存取Java关系数据的企业级标准,JPA通过JDK5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中)规范的基础上封装的一套JPA应用框架,是一种企业级Java的现代数据访问技术,该技术较完美地将Spring的易用性和JPA的强大数据访问功能结合起来,开发者在使用Springdata (是一个用于简化数据库访问,并支持云服务的开源框架,针对关系型数据库、KV数据库、Document数据库、Graph数据库、Map-Reduce等一些主流数据库,采用统一技术进行访问,并且尽可能简化访问手段的技术)时,连仅剩的实现持久层业务逻辑的工作都省了,唯一要做的,就只是声明持久层的接口,其他都交给Spring Data JPA来帮你完成!
[0003]但是Spring data JPA还是有美中不足的地方,就是缺少对动态查询功能。在Spring data中,无论是命名查询还是约定查询,无论你传递的参数是否为空,框架给你构造的查询条件都是一样的。
[0004]但是在实际应用中,很多场景都是需要进行动态查询的,尤其是页面查询功能。在开发Web应用时,页面中会有一系列的查询条件,如果你输入了查询值,就要把该条件加入到查询语句中,否则,忽略该条件。
[0005]如果使用传统技术来进行编程,无论使用Hibernate(—种开放源代码的对象关系映射框架)还是JDBC (Java Data Base Connectivity, Java数据库连接),都需要写大量的逻辑来对查询条件进行判断,并且还要根据返回对象的属性从查询结果中取值、转换。编程不仅繁琐易错,而且后续的可维护性也不强。不具备通用性和灵活性。
[0006]Mybati s3 (支持普通SQL查询、存储过程和高级映射的优秀持久层框架)中比较强大的功能之一就是动态SQL (Structured Query Language,结构化查询语言)语句,类似于脚本语句一样动态进行SQL语句的配置,可以解决这类问题。但是一来这个需要使用Xml来进行配置,并且还是需要写比较复杂的脚本,另外整个编程要使用Mybatis的架构,对于使用hibernate或者JPA架构的开发团队是不适用的。
【发明内容】
[0007]本发明的目的是克服了上述现有技术的缺点,提供了一种能够实现只需要进行接口方法定义声明、而无需进行任何实际编码逻辑的实现、增强软件的维护性、提高软件开发的生产力、具有更广泛应用范围的Spring data JPA实现动态查询的方法。
[0008]为了实现上述目的,本发明的Spring data JPA实现动态查询的方法具有如下构成:
[0009]该Spring data JPA实现动态查询的方法,其主要特点是,所述的方法包括以下步骤:
[0010](I)采用基于Spring FactoryBean创建的动态查询确认类判断被调用的接口方法是否需要进行动态查询,如果是,则继续步骤(3),否则,继续步骤(2);
[0011](2)采用原Spring data框架处理该被调用的接口方法,然后结束退出;
[0012](3)采用基于Spring FactoryBean创建的动态JPA查询类创建动态查询语句;
[0013](4)所述的动态JPA查询类将所述的动态查询语句发送至JPA框架;
[0014](5)所述的动态JPA查询类将JPA框架的查询结果转换成指定类型的返回对象。
[0015]较佳地,所述的判断被调用的接口方法是否需要进行动态查询,具体为:
[0016]判断被调用的接口方法是否进行了动态JPA查询类的注解。
[0017]较佳地,所述的动态JPA查询类创建动态查询语句,具体为:
[0018](31)所述的动态JPA查询类根据接口方法中的参数定义以及进行接口方法调用的实际传递参数值创建动态查询语句中的条件语句。
[0019]更佳地,所述的根据接口方法中的参数定义以及进行接口方法调用的实际传递参数值创建动态查询语句中的条件语句,包括以下步骤:
[)判断所述的接口方法的实际传递参数值是否为空值,如果是,则继续步骤
(312),否则继续步骤(315);
[)判断该实际传递参数是否有默认值,如果是,则继续步骤(313),否则继续步骤(314);
[)将该默认值的条件加入到动态查询语句中的条件语句中;
[)将该实际传递参数对应的查询条件忽略;
[)判断该接口方法中是否有该实际传递参数的动态注解定义,如果是,则继续步骤(316);
[)将该接口方法的条件加入到动态查询语句中的条件语句中。
[0026]更佳地,所述的步骤(31)和(4)之间,还包括以下步骤:
[0027](32)所述的动态JPA查询类根据接口方法中定义的JPA查询结果返回类型创建动态查询语句中的选择部分。
[0028]更进一步地,所述的动态JPA查询类将JPA框架的查询结果转换成指定类型的返回对象,具体为:
[0029]所述的动态JPA查询类根据接口方法中定义的JPA查询结果返回类型将JPA框架的查询结果转换成指定类型的POJO对象。
[0030]更佳地,所述的步骤(31)和(4)之间,还包括以下步骤:
[0031](33)所述的动态JPA查询类根据接口方法定义中是否有Pageable或Sort类型的参数以及对应的实际传递参数值是否为空对动态查询语句中的条件语句进行排序。
[0032]采用了该发明中的Spring data JPA实现动态查询的方法,具有如下有益效果:
[0033]通过使用Spring data的动态查询扩展,在进行页面查询时,只需要对查询结果的VO类、查询条件的VO类进行一些必要的注解,再在接口中进行方法的声明就可以了。如果以后页面查询的需求发生变化,无论是查询条件、排序条件或者返回结果发生变化,只需要在相应的VO类中增加或者修改属性、注解即可。大大减轻了开发人员的负担,降低了进行页面查询开发出错的可能性,增强了软件的维护性,提高了软件开发的生产力。同时也增强了代码的可阅读性、可理解性(因为没有复杂的逻辑),具有更广泛的应用范围。
【专利附图】
【附图说明】
[0034]图1为本发明的Spring data JPA实现动态查询的方法的流程图。
[0035]图2为本发明的Spring data JPA实现动态查询的方法应用于实施例的详细流程图。
【具体实施方式】
[0036]为了能够更清楚地描述本发明的技术内容,下面结合具体实施例来进行进一步的描述。
[0037]本技术是对Spring data JPA技术的扩展,使其能够根据传递查询参数和返回对象的特性动态构造查询语句,并动态返回POJO (plain old java object,简单的Java对象)对象集合。
[0038]本技术主要就是为了解决在Web开发中常见的页面查询问题,对Spring data的框架进行了扩展,使其可以根据查询参数以及返回对象的特性动态构造查询语句,并且将查询到的数据动态地封装到返回结果对象中。该技术完美地融合到Spring data框架中,同样只需要进行接口方法定义声明,而不需要进行任何实际编码逻辑的实现。
[0039]Spring data JPA之所以能够做到只需要进行接口声明而不需要写任何逻辑代码,是使用了 Spring的FactoryBean技术,对接口的调用动态生成代理类,在代理类中根据接口中的注解进行解析、转换,最终将请求转发给JPA框架。
[0040]如图1所示,要使Spring data能够进行动态查询,首先需要对JPARepositoryFactoryBean类进行扩展。当调用定义的接口方法时,扩展的FactoryBean类首先创建一个DelegeatedQueryLookupStrategy实例(动态查询确认类)。该实例首先判断接口方法是否需要进行动态查询,如果不是动态查询的方法,则由原来的Spring data框架来进行处理;如果是动态查询方法,则创建一个DynamicJPAQuery对象实例(动态JPA查询类),由该实例来完成动态创建SQL语句(动态查询语句)并将JPA的查询结果转换成指定的P0J0类对象。
[0041]判断一个接口方法是否是动态查询也很简单,就看该方法是否进行了 @DynamicQuery的注解(Annonation)。DynamicJPAQuery类会根据接口方法中的参数定义以及进行方法调用的实际传递参数值进行SQL语句中的条件语句的动态创建。如果传递的实际参数值不为null,并且方法中有该参数的动态注解定义,就将该条件加入到查询语句中(如果该参数在方法定义中没有动态注解定义,则表示该参数不参与SQL语句的构建,该参数值会被忽略掉)。如果传递的值为null,则看该参数的动态注解是否有默认值,如果有默认值,则将默认值的条件加入到查询语句中,否则则将该参数对应的查询条件忽略掉。
[0042]如果方法接口中定义的返回类型不是JPA的entity类型,则可以根据返回类型的类注解定义动态构建SQL语句中的select部分,并且可以支持比较复杂的统计语句和子查询语句。[0043]另外DynamicJPAQuery还能够动态地构建查询语句中的排序功能,主要是根据方法定义中是否有Pageable或者Sort类型的参数以及传递的实际参数是否为空来进行动态构建。
[0044]该扩展因为完全在Spring data JPA的框架下进行开发的,因此与该框架的融合非常紧密,完全支持Spring data JPA的高级特性,例如可以使用query hint来使动态查询支持cache、transaction等特性。
[0045]我们在实现时还采用了约定优于配置(convention over configuration)的软件开发方法。该方法也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。例如我们会根据接口定义的返回值类型来进行一系列的约定开发。
[0046]如果返回的实体类(entity),则使用JPA Query方法进行调用,如果返回的是一个自定义的VO类,则使用JPA native Query (本地查询)来进行查询。
[0047]如果返回类型是List〈?&类型,则只是简单将返回结果转换为一个列表。
[0048]如果返回的类型是Page〈?&类型,则表示客户既关心查询结果,又关心查询数量,即我们通常意义上所说的分页查询。框架会自动构建查询总数的SQL语句,并且将查询的总数信息封装到Page对象中。但是同时也要求在参数列表中需要有Pagable类型的参数。
[0049]如图2所示,下面是本发明的方法在具体实施例中的应用。
[0050]1、Spring 配置:
[0051]&JPA: repositories base-package= " com.certus.da0.hibernate,query, sample " factory-`class= " com.certus.da0.hibernate, spring.QueryRepositoryFactoryBean" /&
[0052]其中的QueryRepositoryFactoryBean 是对于 Spring factoryBean 类进行的扩展,使其能对定义动态查询的DAO接口进行拦截
[0053]2、定义查询结果V0:
【权利要求】
1.一种Spring data JPA实现动态查询的方法,其特征在于,所述的方法包括以下步骤:
(1)采用基于SpringFactoryBean创建的动态查询确认类判断被调用的接口方法是否需要进行动态查询,如果是,则继续步骤(3),否则,继续步骤(2);
(2)采用原Springdata框架处理该被调用的接口方法,然后结束退出;
(3)采用基于SpringFactoryBean创建的动态JPA查询类创建动态查询语句;
(4)所述的动态JPA查询类将所述的动态查询语句发送至JPA框架;
(5)所述的动态JPA查询类将JPA框架的查询结果转换成指定类型的返回对象。
2.根据权利要求1所述的Springdata JPA实现动态查询的方法,其特征在于,所述的判断被调用的接口方法是否需要进行动态查询,具体为:
判断被调用的接口方法是否进行了动态JPA查询类的注解。
3.根据权利要求1所述的Springdata JPA实现动态查询的方法,其特征在于,所述的动态JPA查询类创建动态查询语句,具体为:
(31)所述的动态JPA查询类根据接口方法中的参数定义以及进行接口方法调用的实际传递参数值创建动态查询语句中的条件语句。
4.根据权利要求3所述的Springdata JPA实现动态查询的方法,其特征在于,所述的根据接口方法中的参数定义以及进行接口方法调用的实际传递参数值创建动态查询语句中的条件语句,包括以下步骤:
(311)判断所述的接口方法的实际传递参数值是否为空值,如果是,则继续步骤(312),否则继续步骤(315);
(312)判断该实际传递参数是否有默认值,如果是,则继续步骤(313),否则继续步骤(314);
(313)将该默认值的条件加入到动态查询语句中的条件语句中;
(314)将该实际传递参数对应的查询条件忽略;
(315)判断该接口方法中是否有该实际传递参数的动态注解定义,如果是,则继续步骤(316);
(316)将该接口方法的条件加入到动态查询语句中的条件语句中。
5.根据权利要求3所述的Springdata JPA实现动态查询的方法,其特征在于,所述的步骤(31)和(4)之间,还包括以下步骤:
(32)所述的动态JPA查询类根据接口方法中定义的JPA查询结果返回类型创建动态查询语句中的选择部分。
6.根据权利要求5所述的Springdata JPA实现动态查询的方法,其特征在于,所述的动态JPA查询类将JPA框架的查询结果转换成指定类型的返回对象,具体为:
所述的动态JPA查询类根据接口方法中定义的JPA查询结果返回类型将JPA框架的查询结果转换成指定类型的POJO对象。
7.根据权利要求3所述的Springdata JPA实现动态查询的方法,其特征在于,所述的步骤(31)和(4)之间,还包括以下步骤:
(33)所述的动态JPA查询类根据接口方法定义中是否有Pageable或Sort类型的参数以及对应的实际传递参数值是否为空对动态查询语句中的条件语句进行排序。
【文档编号】G06F17/30GKSQ
【公开日】日
申请日期:日
优先权日:日
【发明者】逯利军, 钱培专, 汪金忠, 林强, 李克民, 宋聚平, 卢天华
申请人:赛特斯信息科技股份有限公司查询、排序.....(转) -
- ITeye技术网站
博客分类:
JPQL就是一种查询语言,具有与 SQL 相类似的特征, JPQL 是完全面向对象的,具备继承、多态和关联等特性,和hibernate HQL很相似。
查询语句的参数
JPQL 语句支持两种方式的参数定义方式 : 命名参数和位置参数 。 。在同一个查询语句中只允许使用一种参数定义方式。
命令参数的格式为:“ : + 参数名”
Query query = em.createQuery("select p from Person p where p.personid=:Id ");
query.setParameter("Id",new Integer(1));
位置参数的格式为“ ?+ 位置编号”
Query query = em.createQuery("select p from Person p where p.personid=?1 ");
query.setParameter(1,new Integer(1));
如果你需要传递 java.util.Date 或 java.util.Calendar 参数进一个参数查询 ,你需要使用一个特殊的 setParameter() 方法 ,相关的 setParameter 方法定义如下:
public interface Query
// 命名参数查询时使用,参数类型为 java.util.Date
Query setParameter(String name, java.util.Date value, TemporalType temporalType);
// 命名参数查询时使用,参数类型为 java.util.Calendar
Query setParameter(String name, Calendar value, TemporalType temporalType);
// 位置参数查询时使用,参数类型为 java.util.Date
Query setParameter(int position, Date value, TemporalType temporalType);
// 位置参数查询时使用,参数类型为 java.util.Calendar
Query setParameter(int position, Calendar value, TemporalType temporalType);
因为一个 Date 或 Calendar 对象能够描述一个真实的日期、时间或时间戳 . 所以我们需要告诉 Query 对象怎么使用这些参数,我们把 javax.persistence.TemporalType 作为参数传递进 setParameter 方法,告诉查询接口在转换 java.util.Date 或 java.util.Calendar 参数到本地 SQL 时使用什么数据库类型 。
下面通过实例来学习JPQL语句,例子的entity Bean 有 Person, Order, OrderItem , 他们之间的关系是:一个 Person 有多个 Order, 一个 Order 有多个 OrderItem 。
JPQL语句的大小写敏感性:除了 Java 类和属性名称外,查询都是大小写不敏感的 。所以, SeLeCT 和 sELEct 以及 SELECT 相同的,但是 com.foshanshop.ejb3.bean.Person 和 com.foshanshop.ejb3.bean.PERSon 是不同的, person.name 和 person.NAME 也是不同的。
可以在实体 bean 上通过 @NamedQuery or @NamedQueries 预先定义一个或多个查询语句,减少每次因书写错误而引起的 BUG 。通常把经常使用的查询语句定义成命名查询 。
定义单个命名查询:
@NamedQuery (name= "getPerson" , query= "FROM Person WHERE personid=?1" )
public class Person implements Serializable{
如果要定义多个命名查询,应在 @javax.persistence.NamedQueries里定义 @NamedQuery :
@NamedQueries ({
@NamedQuery (name= "getPerson" , query= "FROM Person WHERE personid=?1" ),
@NamedQuery (name= "getPersonList" , query= "FROM Person WHERE age&?1" )
public class Person implements Serializable{
当命名查询定义好了之后,我们就可以通过名称执行其查询。代码如下:
Query query = em . createNamedQuery("getPerson") ;
query.setParameter(1, 1);
排序 (order by)
"ASC" 和 "DESC" 分别为升序和降序, JPQL 中默认为 asc 升序
// 先按年龄降序排序,然后按出生日期升序排序
Query query = em.createQuery("select p from Person p order by p.age desc, p.birthday asc ");
查询部分属性
通常来说,都是针对 Entity 类的查询,返回的也是被查询的 Entity 类的实体。J P QL 也允许我们直接查询返回我们需要的属性,而不是返回整个 Entity 。在一些 Entity 中属性特别多的情况,这样的查询可以提高性能
// 只 查询我们感兴趣的属性 ( 列 )
Query query=em.createQuery("select p.personid, p.name from Person p order by p.personid desc ");
// 集合中的元素不再是 Person, 而是一个 Object[] 对象数组
List result = query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
Object[] row = ( Object[]) iterator.next();
int personid = Integer.parseInt(row[0].toString());
String PersonName = row[1].toString();
查询中使用构造器 (Constructor)
JPQL 支持将查询的属性结果直接作为一个 java class 的构造器参数,并产生实体作为结果返回 。例如上面的例子只获取 person entity bean的name and personid属性,我们不希望返回的集合的元素是object[],而希望用一个类来包装它。就要用到使用构造器 。
public class SimplePerson {
private String name ;
public SimplePerson() {
public SimplePerson(Integer personid, String name) {
this . name =
this . personid =
查询代码为:
// 我们把需要的两个属性作为 SimplePerson 的构造器参数,并使用 new 函数。
Query query = em.createQuery(" select new com.foshanshop.ejb3.bean.SimplePerson(p. personid, p.name) from Person p order by p.personid desc ");
// 集合中的元素是 SimplePerson 对象
List result = query.getResultList();
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
SimplePerson simpleperson = (SimplePerson) iterator.next();
聚合查询 (Aggregation)
JPQL 支持的聚合函数 包括:
3. COUNT() ,返回类型为 Long ,注意 count(*) 语法在 hibernate 中可用,但在 toplink 其它产品中并不可用
// 获取最大年龄
Query query = em.createQuery("select max(p.age) from Person p");
Object result = query.getSingleResult();
String maxAge = result.toString();
// 获取平均年龄
query = em.createQuery("select avg(p.age) from Person p");
// 获取最小年龄
query = em.createQuery("select min(p.age) from Person p");
// 获取总人数
query = em.createQuery("select count(p) from Person p");
// 获取年龄总和
query = em.createQuery("select sum(p.age) from Person p");
如果聚合函数不是 select...from 的唯一一个返回列,需要使用 "GROUP BY" 语句 。 "GROUP BY" 应该包含 select 语句中除了聚合函数外的所有属性。
// 返回男女生各自的总人数
Query query = em.createQuery(" select p.sex, count(p) from Person p group by p.sex ");
// 集合中的元素不再是 Person, 而是一个 Object[] 对象数组
List result = query.getResultList();
如果还需要加上查询条件,需要使用 "HAVING" 条件语句而不是 "WHERE" 语句
// 返回人数超过 1 人的性别
Query query = em.createQuery("select p.sex, count(p) from Person p group by p.sex having count(*) &?1");
// 设置查询中的参数
query.setParameter(1, new Long(1));
// 集合中的元素不再是 Person, 而是一个 Object[] 对象数组
List result = query.getResultList();
关联 (join)
JPQL 仍然支持和 SQL 中类似的关联语法:
left out join/left join
inner join
left join fetch/inner join fetch
left out join/left join 等,都是允许符合条件的右边表达式中的 Entiies 为空( 需要显式使用 left join/left outer join 的情况会比较少。 )
// 获取 26 岁人的订单 , 不管 Order 中是否有 OrderItem
select o from Order o left join o.orderItems where o.ower.age=26 order by o.orderid
inner join 要求右边的表达式必须返回 Entities 。
// 获取 26 岁人的订单 ,Order 中必须要有 OrderItem
select o from Order o inner join o.orderItems where o.ower.age=26 order by o.orderid
!!重要知识点 : 在默认的查询中, Entity 中的集合属性默认不会被关联,集合属性默认是延迟加载 ( lazy-load ) 。那么, left fetch/left out fetch/inner join fetch 提供了一种灵活的查询加载方式来提高查询的性能。
private String QueryInnerJoinLazyLoad(){
// 默认不关联集合属性变量 (orderItems) 对应的表
Query query = em.createQuery("select o from Order o inner join o.orderItems where o.ower.age=26 order by o.orderid");
List result = query.getResultList();
if (result!=null && result.size()&0){
// 这时获得 Order 实体中 orderItems( 集合属性变量 ) 为空
Order order = (Order) result.get(0);
// 当需要时, EJB3 Runtime 才会执行一条 SQL 语句来加载属于当前 Order 的
//OrderItems
Set&OrderItem& list = order.getOrderItems ();
Iterator&OrderItem& iterator = list.iterator();
if (iterator.hasNext()){
OrderItem orderItem =iterator.next();
System.out.println (" 订购产品名: "+ orderItem.getProductname());
上面代码在执行 "select o from Order o inner join o.orderItems where o.ower.age=26 order by o.orderid" 时编译成的 SQL 如下( 他不包含集合属性变量 (orderItems) 对应表的字段 ) :
select order0_.orderid as orderid6_, order0_.amount as amount6_, order0_.person_id as
person4_6_, order0_.createdate as createdate6_ from Orders order0_ inner join OrderItems
orderitems1_ on order0_.orderid=orderitems1_.order_id, Person person2_ where
order0_.person_id=person2_.personid and person2_.age=26 order by order0_.orderid
上面代码当执行到 Set&OrderItem& list = order.getOrderItems(); 时才会执行一条 SQL 语句来加载 属于当前 Order 的 OrderItems ,编译成的 SQL 如下 :
select orderitems0_.order_id as order4_1_, orderitems0_.id as id1_, orderitems0_.id as id7_0_,
orderitems0_.order_id as order4_7_0_, orderitems0_.productname as productn2_7_0_,
orderitems0_.price as price7_0_ from OrderItems orderitems0_ where orderitems0_.order_id=?
order by orderitems0_.id ASC
这样的查询性能上有不足的地方 。为了查询 N 个 Order ,我们需要一条 SQL 语句获得所有的 Order 的原始对象属性,但需要另外 N 条语句获得每个 Order 的 orderItems 集合属性。为了避免 N+1 的性能问题,我们可以利用 join fetch 一次过用一条 SQL 语句把 Order 的所有信息查询出来
// 获取 26 岁人的订单 ,Order 中必须要有 OrderItem
Query query = em.createQuery("select o from Order o inner join fetch o.orderItems where
o.ower.age=26 order by o.orderid");
上面这句HPQL编译成以下的 SQL :
select order0_.orderid as orderid18_0_, orderitems1_.id as id19_1_, order0_.amount as
amount18_0_,order0_.person_id as person4_18_0_, order0_.createdate as createdate18_0_,
orderitems1_.order_id as order4_19_1_, orderitems1_.productname as productn2_19_1_,
orderitems1_.price as price19_1_, orderitems1_.order_id as order4_0__, orderitems1_.id as id0__
from Orders order0_ inner join OrderItems orderitems1_ on
order0_.orderid=orderitems1_.order_id, Person person2_ where
order0_.person_id=person2_.personid and person2_.age=26 order by order0_.orderid,
orderitems1_.id ASC
上面由于使用了 fetch, 这个查询只会产生一条 SQL 语句,比原来需要 N+1 条 SQL 语句在性能上有了极大的提升
排除相同的记录 DISTINCT
使用关联查询,我们很经常得到重复的对象,如下面语句:
" select o from Order o inner join fetch o.orderItems order by o.orderid "
当有 N 个 orderItem 时就会产生 N 个 Order, 而有些 Order 对象往往是相同的,这时我们需要使用 DISTINCT 关键字来排除掉相同的对象 。
select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid
比较 Entity
在查询中使用参数查询时 ,参数类型除了 String, 原始数据类型 ( int, double 等 ) 和它们的对象类型 ( Integer, Double 等 ), 也可以是 Entity 的实例 。
// 查询某人的所有订单
Query query = em.createQuery("select o from Order o where o.ower =?1 order by o.orderid");
Person person = new Person();
person.setPersonid(new Integer(1));
// 设置查询中的参数
query.setParameter(1,person);
批量更新 (Batch Update)
HPQL支持批量更新
// 把所有订单的金额加 10
Query query = em.createQuery(" update Order as o set o.amount=o.amount+10 ");
//update 的记录数
int result = query. executeUpdate ();
批量删除 (Batch Remove)
// 把金额小于100的订单删除,先删除订单子项, 再删除订单
Query query = em .createQuery( "delete from OrderItem item where item.order in(from Order as o where o.amount&100)" );
query. executeUpdate();
query = em .createQuery( "delete from Order as o where o.amount&100" );
query.executeUpdate(); //delete 的记录数
使用操作符 NOT
// 查询除了指定人之外的所有订单
Query query = em.createQuery("select o from Order o where not(o.ower =?1) order by o.orderid");
Person person = new Person();
person.setPersonid(new Integer(2));
// 设置查询中的参数
query.setParameter(1,person);
使用操作符 BETWEEN
select o from Order as o where o.amount between 300 and 1000
使用操作符 IN
// 查找年龄为 26,21 的 Person
select p from Person as p where p.age in(26,21)
使用操作符 LIKE
// 查找以字符串 "li" 开头的 Person
select p from Person as p where p.name like 'li%'
使用操作符 IS NULL
// 查询含有购买者的所有 Order
select o from Order as o where o.ower is [not] null
使用操作符 IS EMPTY
IS EMPTY 是针对集合属性 (Collection) 的操作符 。可以和 NOT 一起使用 。 注:低版权的 Mysql 不支持 IS EMPTY
// 查询含有订单项的所有 Order
select o from Order as o where o.orderItems is [not] empty
使用操作符 EXISTS
[NOT]EXISTS 需要和子查询配合使用 。注:低版权的 Mysql 不支持 EXISTS
// 如果存在订单号为 1 的订单,就获取所有 OrderItem
select oi from OrderItem as oi where exists (select o from Order o where o.orderid=1)
// 如果不存在订单号为 10 的订单,就获取 id 为 1 的 OrderItem
select oi from OrderItem as oi where oi.id=1 and not exists (select o from Order o where o.orderid=10)
字符串函数
JPQL 定义了内置函数方便使用。这些函数的使用方法和 SQL 中相应的函数方法类似。包括:
1. CONCAT 字符串拼接
2. SUBSTRING 字符串截取
3. TRIM 去掉空格
4. LOWER 转换成小写
5. UPPER 装换成大写
6. LENGTH 字符串长度
7. LOCATE 字符串定位
// 查询所有人员,并在姓名后面加上字符串 "_foshan"
select p.personid, concat(p.name, '_foshan') from Person as p
// 查询所有人员 , 只取姓名的前三个字符
select p.personid, substring (p.name,1,3) from Person as p
HPQL 定义的计算函数包括:
ABS 绝对值
SQRT 平方根
MOD 取余数
SIZE 取集合的数量
// 查询所有 Order 的订单号及其订单项的数量
select o.orderid, size (o.orderItems) from Order as o group by o.orderid
// 查询所有 Order 的订单号及其总金额 /10 的余数
select o.orderid, mod (o.amount, 10) from Order as o
子查询可以用于 WHERE 和 HAVING 条件语句中
// 查询年龄为 26 岁的购买者的所有 Order
select o from Order as o where o.ower in(select p from Person as p where p.age =26)
结果集分页
有些时候当执行一个查询会返回成千上万条记录,事实上我们只需要显示一部分数据。这时我们需要对结果集进行分页 , QueryAPI 有两个接口方法可以解决这个问题: setMaxResults( ) 和 setFirstResult( ) 。
setMaxResults 方法设置获取多少条记录
setFirstResult 方法设置从结果集中的那个索引开始获取 (假如返回的记录有 3 条,容器会自动为记录编上索引,索引从 0 开始,依次为 0 , 1 , 2 )
public List getPersonList( int max, int whichpage) {
int index = (whichpage-1) *
Query query = em .createQuery( "from Person p order by personid asc" );
List list = query. setMaxResults(max) .
setFirstResult(index) .
getResultList();
em .clear(); // 分离内存中受EntityManager管理的实体bean,让VM进行垃圾回收
浏览 42063
string2020 写道in (1,2,3)请问,这种参数,如何用query.setParameter("Id",new Integer(1));的方式设置参数先放入集合,再用setParameter("Id",集合名),见/questions//how-to-delete-items-by-id-s-of-their-child-item-in-hibernate不行,我试过了。只能在hibernate里面可以。jpa不行
in (1,2,3)请问,这种参数,如何用query.setParameter("Id",new Integer(1));的方式设置参数先放入集合,再用setParameter("Id",集合名),见/questions//how-to-delete-items-by-id-s-of-their-child-item-in-hibernate
浏览: 140539 次
来自: 成都
&div class=&quote_title ...
&div class=&quote_title ...
感谢你的分享,解决了我的问题啊
in (1,2,3)请问,这种参数,如何用query.setP ...
首先你违反了JPA的主键生成策略,JPA在第一次执行inser ...

我要回帖

更多关于 spring boot data jpa 的文章

 

随机推荐