`

jpa

jpa 
阅读更多

序言

自工作以来,除了以前比较流量的hibernate,就是一直使用ORM 规范 JPA了.而这几天工作需要,研究了下JPA的标准查询,名为:JPA criteria查询.相比JPQL,其优势是类型安全,更加的面向对象.

使用标准查询,开发人员可在编译的时候就检查查询的正确与否.而以前也只是在Hibernate中听说有过.具体不详,没用过.

用的maven插件生成的.具体看这些把.
Hibernate

org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor
http://relation.to/Bloggers/HibernateStaticMetamodelGeneratorAnnotationProcessor
OpenJPA

org.apache.openjpa.persistence.meta.AnnotationProcessor6
http://openjpa.apache.org/builds/latest/docs/manual/manual.html#d0e11094
DataNucleus

org.datanucleus.jpa.JPACriteriaProcessor
http://www.datanucleus.org/products/accessplatform_2_1/jpa/jpql_criteria_metamodel.html

一.JPA元模型概念,及使用

在JPA中,标准查询是以元模型的概念为基础的.元模型是为具体持久化单元的受管实体定义的.这些实体可以是实体类,嵌入类或者映射的父类.提供受管实体元信息的类就是元模型类.

描述受管类的状态和他们之间的关系的静态元模型类可以

  • 1.从注解处理器产生
  • 2.从程序产生
  • 3.用EntityManager访问.

如下code,一个简单的实体类package com.demo.entities;下,实体类Employee ,假设该实体有诸如id,name和age的基本属性,还有与类Address的OneToMany关联:

@Entity
@Table
public class Employee{  
	private int id;   
	private String name;
	private int age;
	@OneToMany
	private List<Address> addresses;
	// Other code…
}

Employee类(com.demo.entities包中定义)的标准元模型类的名字将是使用 javax.persistence.StaticMetamodel注解的Employee_。元模型类的属性全部是static和public的。Employee的每一个属性都会使用在JPA2规范中描述的以下规则在相应的元模型类中映射:

  • 诸如id,name和age的非集合类型,会定义静态属性SingularAttribute<A, B> b,这里b是定义在类A中的类型为B的一个对象。
  • 对于Addess这样的集合类型,会定义静态属性ListAttribute<A, B> b,这里List对象b是定义在类A中类型B的对象。其它集合类型可以是SetAttribute, MapAttribute 或 CollectionAttribute 类型。

 以下是用注解处理器产生的元模型类package com.demo.entities;下:

import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@Generated("org.hibernate.jpamodelgen.JPAMetaModelEntityProcesso")
@StaticMetamodel(Employee.class)
public classEmployee_ {     
	public static volatile SingularAttribute<Employee, Integer> id;   
	public static volatile SingularAttribute<Employee, Integer> age;   
	public static volatile SingularAttribute<Employee, String> name;    
	public static volatile ListAttribute<Employee, Address> addresses;
}

就像它的名字表明的,注解处理器处理注解,帮助产生源代码注解处理在编译时就能激活。元模型类遵循JPA2.0规范中为定义标准元模型类而描述的规则创建。

使用元模型类最大的优势是凭借其实例化可以在编译时访问实体的持久属性.该特性使得criteria 查询更加类型安全.

元模型API与Java中的标准反射API密切相关。主要不同在于使用标准反射API编译器无法验证其正确性。例如:下面的代码会通过编译测试:

Class myClass = Class.forName("com.demo.Test");
Field myField = myClass.getField("myName");

编译器假定com.demo.Test中定义了属性myName,一旦该类并没有定义属性myName,编译器将抛出运行时异常。

元模型API会强制编译器检查适当的值是否分配给实体类的持久属性。例如:考虑Employee类的age属性,它是Integer变量。若该属性被赋值为String类型的值,编译器会抛出错误。该实现并不要求支持非标准特性。程序员编写的元模型类通常称为非标准元模型类。当EntityManagerFactory 创建时,持久化提供者会初始化元模型类的属性。

二.使用criteria 查询简单Demo

为了更好的理解criteria 查询,考虑拥有Employee实例集合的Dept实体,Employee和Dept的元模型类的代码如下:

//All Necessary Imports
@StaticMetamodel(Dept.class)
public classDept_ {    
	public static volatile SingularAttribute<Dept, Integer> id;   
	public static volatile ListAttribute<Dept, Employee> employeeCollection;    
	public static volatile SingularAttribute<Dept, String> name;
}
//All Necessary Imports
@StaticMetamodel(Employee.class)
public classEmployee_ {     
	public static volatile SingularAttribute<Employee, Integer> id;    
	public static volatile SingularAttribute<Employee, Integer> age;    
	public static volatile SingularAttribute<Employee, String> name;    
	public static volatile SingularAttribute<Employee, Dept> deptId;
}

下面的代码片段展示了一个criteria 查询,它用于获取所有年龄大于24岁的员工:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> employee = criteriaQuery.from(Employee.class);
Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);
criteriaQuery.where(condition);
TypedQuery<Employee> typedQuery = em.createQuery(criteriaQuery);
List<Employee> result = typedQuery.getResultList();

对应的SQL: SELECT * FROM employee WHERE age > 24

三.构建CriteriaQuery 实例API说明

1.CriteriaBuilder 安全查询创建工厂,创建CriteriaQuery,创建查询具体具体条件Predicate 等

CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.可以从EntityManager 或 EntityManagerFactory类中获得CriteriaBuilder. 
比如: CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();

2.CriteriaQuery 安全查询主语句

CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
它通过调用 CriteriaBuilder, createQuery 或CriteriaBuilder.createTupleQuery 获得。
CriteriaBuilder就像CriteriaQuery 的工厂一样。
CriteriaBuilder工厂类是调用EntityManager.getCriteriaBuilder 或 EntityManagerFactory.getCriteriaBuilder而得。 
Employee实体的 CriteriaQuery 对象以下面的方式创建:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);

3.Root 定义查询的From子句中能出现的类型

AbstractQuery是CriteriaQuery 接口的父类。它提供得到查询根的方法。 
Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似。 
Root实例也是类型化的,且定义了查询的FROM子句中能够出现的类型。 
查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。 
Criteria查询,可以有多个查询根。 
Employee实体的查询根对象可以用以下的语法获得 : 

Root<Employee> employee = criteriaQuery.from(Employee.class);

 

4.Predicate 过滤条件

过滤条件应用到SQL语句的FROM子句中。 
在criteria 查询中,查询条件通过Predicate 或Expression 实例应用到CriteriaQuery 对象上。 
这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上。 
CriteriaBuilder 也是作为Predicate 实例的工厂,Predicate 对象通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建。 
Predicate 实例也可以用Expression 实例的 isNull, isNotNull 和 in方法获得,复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。 
下面的代码片段展示了Predicate 实例检查年龄大于24岁的员工实例:

Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);
criteriaQuery.where(condition);

过Employee_元模型类age属性,称之为路径表达式。若age属性与String文本比较,编译器会抛出错误,这在JPQL中是不可能的。

5.Predicate[] 多个过滤条件

List<Predicate> predicatesList = new ArrayList<Predicate>();

predicatesList.add(.....Pridicate....)

criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));

OR语句

 

predicatesList.add(criteriaBuilder.or(criteriaBuilder.equal(root.get(RepairOrder_.localRepairStatus), LocalRepairStatus.repairing),criteriaBuilder.equal(root.get(RepairOrder_.localRepairStatus), LocalRepairStatus.diagnos)));

 

忽略大小写(全大写)

 

predicatesList.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(RepairShop_.shopName)), StringUtils.upperCase(StringUtils.trim(this.shopName)) + "%"));

通过如上两句添加多个.

 

6.TypedQuery执行查询与获取元模型实例

注意,你使用EntityManager创建查询时,可以在输入中指定一个CriteriaQuery对象,它返回一个TypedQuery,它是JPA 2.0引入javax.persistence.Query接口的一个扩展,TypedQuery接口知道它返回的类型。

所以使用中,先创建查询得到TypedQuery,然后通过typeQuery得到结果.

当EntityManager.createQuery(CriteriaQuery)方法调用时,一个可执行的查询实例会创建,该方法返回指定从 criteria 查询返回的实际类型的TypedQuery 对象。

TypedQuery 接口是javax.persistence.Queryinterface.的子类型。在该片段中, TypedQuery 中指定的类型信息是Employee,调用getResultList时,查询就会得到执行 
TypedQuery<Employee> typedQuery = em.createQuery(criteriaQuery);
List<Employee> result = typedQuery.getResultList();
元模型实例通过调用 EntityManager.getMetamodel 方法获得,EntityType<Employee>的元模型实例通过调用Metamodel.entity(Employee.class)而获得,其被传入 CriteriaQuery.from 获得查询根。

Metamodel metamodel = em.getMetamodel();EntityType<Employee> 
Employee_ = metamodel.entity(Employee.class);
Root<Employee> empRoot = criteriaQuery.from(Employee_);

也有可能调用Root.getModel方法获得元模型信息。类型 EntityType<Dept>的实例Dept_和name属性可以调用getSingularAttribute 方法获得,它与String文本进行比较:

CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root<Dept> dept = criteriaQuery.from(Dept.class);
EntityType<Dept> Dept_ = dept.getModel();
Predicate testCondition = criteriaBuilder.equal(dept.get(Dept_.getSingularAttribute("name", String.class)), "Ecomm");

7.Expression 用在查询语句的select,where和having子句中,该接口有 isNull, isNotNull 和 in方法

Expression对象用在查询语句的select,where和having子句中,该接口有 isNull, isNotNull 和 in方法,下面的代码片段展示了Expression.in的用法,employye的年龄检查在20或24的。 

CriteriaQuery<Employee> criteriaQuery = criteriaBuilder .createQuery(Employee.class);
Root<Employee> employee = criteriaQuery.from(Employee.class);
criteriaQuery.where(employee.get(Employee_.age).in(20, 24));
em.createQuery(criteriaQuery).getResultList();

对应的 SQL: SELECT * FROM employee WHERE age in (20, 24)

下面也是一个更贴切的例子:

 

//定义一个Expression
Expression<String> exp = root.get(Employee.id);
//
List<String> strList=new ArrayList<>();	
strList.add("20");
strList.add("24");		
predicatesList.add(exp.in(strList));

criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));

8.复合谓词

Criteria Query也允许开发者编写复合谓词,通过该查询可以为多条件测试下面的查询检查两个条件。首先,name属性是否以M开头,其次,employee的age属性是否是25。逻辑操作符and执行获得结果记录。

criteriaQuery.where(
 criteriaBuilder.and(
  criteriaBuilder.like(employee.get(Employee_.name), "M%"), 
  criteriaBuilder.equal(employee.get(Employee_.age), 25)
));
em.createQuery(criteriaQuery).getResultList();

连接查询

在SQL中,连接跨多张表以获取查询结果,类似的实体连接通过调用 From.join 执行,连接帮助从一个实体导航到另一个实体以获得查询结果。 
Root的join方法返回一个 Join<Dept, Employee>类型(也可以是SetJoin,,ListJoin,MapJoin 或者 CollectionJoin类型)。

默认情况下,连接操作使用内连接,而外连接可以通过在join方法中指定JoinType参数为LEFT或RIGHT来实现。

CriteriaQuery<Dept> cqDept = criteriaBuilder.createQuery(Dept.class);
Root<Dept> deptRoot = cqDept.from(Dept.class);
Join<Dept, Employee> employeeJoin = deptRoot.join(Dept_.employeeCollection);
cqDept.where(criteriaBuilder.equal(employeeJoin.get(Employee_.deptId).get(Dept_.id), 1));
TypedQuery<Dept> resultDept = em.createQuery(cqDept);

抓取连接

当涉及到collection属性时,抓取连接对优化数据访问是非常有帮助的。这是通过预抓取关联对象和减少懒加载开销而达到的。 
使用 criteria 查询,fetch方法用于指定关联属性 
Fetch连接的语义与Join是一样的,因为Fetch操作不返回Path对象,所以它不能将来在查询中引用。 
在以下例子中,查询Dept对象时employeeCollection对象被加载,这不会有第二次查询数据库,因为有懒加载。

CriteriaQuery<Dept> d = cb.createQuery(Dept.class);
Root<Dept> deptRoot = d.from(Dept.class);
deptRoot.fetch("employeeCollection", JoinType.LEFT);
d.select(deptRoot);
List<Dept> dList = em.createQuery(d).getResultList();

对应SQL: SELECT * FROM dept d, employee e  WHERE d.id = e.deptId

路径表达式

Root实例,Join实例或者从另一个Path对象的get方法获得的对象使用get方法可以得到Path对象,当查询需要导航到实体的属性时,路径表达式是必要的。 
Get方法接收的参数是在实体元模型类中指定的属性。 
Path对象一般用于Criteria查询对象的select或where方法。例子如下:

CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class);
Root<Dept> root = criteriaQuery.from(Dept.class);
criteriaQuery.select(root.get(Dept_.name));&nbsp;

参数化表达式

     在JPQL中,查询参数是在运行时通过使用命名参数语法(冒号加变量,如 :age)传入的。在Criteria查询中,查询参数是在运行时创建ParameterExpression对象并为在查询前调用TypeQuery,setParameter方法设置而传入的。下面代码片段展示了类型为Integer的ParameterExpression age,它被设置为24:

ParameterExpression<Integer> age = criteriaBuilder.parameter(Integer.class);
Predicate condition = criteriaBuilder.gt(testEmp.get(Employee_.age), age);
criteriaQuery.where(condition);
TypedQuery<Employee> testQuery = em.createQuery(criteriaQuery);
List<Employee> result = testQuery.setParameter(age, 24).getResultList();
Corresponding SQL: SELECT * FROM Employee WHERE age = 24;

排序结果

     Criteria查询的结果能调用CriteriaQuery.orderBy方法排序,该方法接收一个Order对象做为参数。通过调用  CriteriaBuilder.asc 或 CriteriaBuilder.Desc,Order对象能被创建。以下代码片段中,Employee实例是基于age的升序排列。  

CriteriaQuery<Employee> criteriaQuery = criteriaBuilder .createQuery(Employee.class);
 Root<Employee> employee = criteriaQuery.from(Employee.class);
 criteriaQuery.orderBy(criteriaBuilder.asc(employee.get(Employee_.age)));
  em.createQuery(criteriaQuery).getResultList();

  对应  SQL: SELECT * FROM Employee ORDER BY age ASC

分组

CriteriaQuery 实例的groupBy 方法用于基于Expression的结果分组。查询通过设置额外表达式,以后调用having方法。下面代码片段中,查询按照Employee类的name属性分组,且结果以字母N开头: 
CriteriaQuery<Tuple> cq = criteriaBuilder.createQuery(Tuple.class); 

Root<Employee> employee = cq.from(Employee.class);
  cq.groupBy(employee.get(Employee_.name));
  cq.having(criteriaBuilder.like(employee.get(Employee_.name), "N%"));
cq.select(criteriaBuilder.tuple(employee.get(Employee_.name),criteriaBuilder.count(employee)));
  TypedQuery<Tuple> q = em.createQuery(cq);
  List<Tuple> result = q.getResultList();

对应  SQL:    SELECT name, COUNT(*) FROM employeeGROUP BY name HAVING name like 'N%'

查询投影

Criteria查询的结果与在Critiria查询创建中指定的一样。结果也能通过把查询根传入 CriteriaQuery.select中显式指定。Criteria查询也给开发者投影各种结果的能力。 

使用construct()

使用该方法,查询结果能由非实体类型组成。在下面的代码片段中,为EmployeeDetail类创建了一个Criteria查询对象,而EmployeeDetail类并不是实体类型。 

CriteriaQuery<EmployeeDetails> criteriaQuery = criteriaBuilder.createQuery(EmployeeDetails.class);
  Root<Employee> employee = criteriaQuery.from(Employee.class);
  criteriaQuery.select(criteriaBuilder.construct(EmployeeDetails.class, employee.get(Employee_.name), employee.get(Employee_.age)));
  em.createQuery(criteriaQuery).getResultList();
  Corresponding SQL: SELECT name, age FROM employee<span style="white-space: normal;">&nbsp;</span>

返回Object[]的查询

Criteria查询也能通过设置值给CriteriaBuilder.array方法返回Object[]的结果。下面的代码片段中,数组大小是2(由String和Integer组成)。 

CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);
  Root<Employee> employee = criteriaQuery.from(Employee.class);
  criteriaQuery.select(criteriaBuilder.array(employee.get(Employee_.name), employee.get(Employee_.age)));
  em.createQuery(criteriaQuery).getResultList();

对应  SQL: SELECT name, age FROM employee

返回元组(Tuple)的查询

数据库中的一行数据或单个记录通常称为元组。通过调用CriteriaBuilder.createTupleQuery()方法,查询可以用于元组上。CriteriaQuery.multiselect方法传入参数,它必须在查询中返回。 

CriteriaQuery<Tuple> criteriaQuery = criteriaBuilder.createTupleQuery();
   Root<Employee> employee = criteriaQuery.from(Employee.class);
   criteriaQuery.multiselect(employee.get(Employee_.name).alias("name"), employee.get(Employee_.age).alias("age"));
   em.createQuery(criteriaQuery).getResultList();

对应 SQL: SELECT name, age FROM employee 

结论

     Criteria查询是一种以更加面向对象的方式查询数据库的方法、在本文中,我讨论了JPA2中类型安全的Criteria查询,以及对于理解Criteria查询非常重要的元模型的概念。也讨论了Criteria查询中的各种API。

转的,觉得写的不错,整理到一个文章里了,地址:http://tanlan.iteye.com/blog/1101110

分享到:
评论

相关推荐

    黎活明老师讲解的JPA源码

    JPA用于整合现有的ORM技术,可以简化现有Java EE和Java SE应用的对象持久化的开发工作,实现ORM的统一。JPA作为一项对象持久化的标准,不但可以获得Java EE应用服务器的支持,还可以直接在Java SE中使用。JPA必将...

    spring-data-jpa-2.0.9.RELEASE-API文档-中文版.zip

    赠送jar包:spring-data-jpa-2.0.9.RELEASE.jar; 赠送原API文档:spring-data-jpa-2.0.9.RELEASE-javadoc.jar; 赠送源代码:spring-data-jpa-2.0.9.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-data-jpa...

    hibernate-jpa-2.1-api-1.0.2.Final-API文档-中文版.zip

    赠送jar包:hibernate-jpa-2.1-api-1.0.2.Final.jar; 赠送原API文档:hibernate-jpa-2.1-api-1.0.2.Final-javadoc.jar; 赠送源代码:hibernate-jpa-2.1-api-1.0.2.Final-sources.jar; 赠送Maven依赖信息文件:...

    hibernate-jpa-2.1-api-1.0.2.Final-API文档-中英对照版.zip

    赠送jar包:hibernate-jpa-2.1-api-1.0.2.Final.jar; 赠送原API文档:hibernate-jpa-2.1-api-1.0.2.Final-javadoc.jar; 赠送源代码:hibernate-jpa-2.1-api-1.0.2.Final-sources.jar; 赠送Maven依赖信息文件:...

    Spring Data JPA从入门到精通

    'SpringDataJPA从入门到精通'分为12章 内容包括整体认识JPA、JPA基础查询方法、定义查询方法、注解式查询方法、@Entity实例里面常用注解详解、JpaRepository扩展详解、JPA的MVC扩展REST支持、DataSource的配置、乐观...

    JPA

    JPA

    尚硅谷JPA视频教程

    JPA视频_PPT及源码 · 1. JPA视频_概述 · 2. JPA视频_HelloWorld · 3. JPA视频_基本注解 · 4. JPA视频_Transient注解 · 5. JPA视频_Temporal注解 · 6. JPA视频_Table主键生成策略 · 7. JPA视频_...

    JPA 标注 JPA标签手册

    这是JPA标签配置手册,从ORACLE官方得到的资料,里面对JPA所有的标签都进行详细的说明。 Index of Annotations • A o @AssociationOverride o @AssociationOverrides o @AttributeOverride o @AttributeOverrides ...

    JPA源文件/jpa学习

    jpa jpa规范 jpa源码 jpa jpa规范 jpa源码

    Spring Data JPA API(Spring Data JPA 开发文档).CHM

    Spring Data JPA API。 Spring Data JPA 开发文档。 官网 Spring Data JPA API。

    JPA教程JPA教程JPA教程

    JPA教程JPA教程JPA教程JPA教程JPA教程

    spring data jpa 教程

    第一章:Spring Data JPA入门 包括:是什么、能干什么、有什么、HelloWorld等 第二章:JpaRepository基本功能 包括:代码示例JpaRepository提供的CRUD功能,还有翻页、排序等功能 第三章:JpaRepository的查询 ...

    spring注解+spring data jpa文档+JPA文档.rar

    spring注解完整版+spring data jpa官方文档中文翻译+JPA2.0官方文档 文档内容齐全 值得参考学习

    SpringBoot + Vue +JPA 的实验室助理工作管理系统课程设计

    该项目是基于SpringBoot + Vue +JPA 的实验室助理信息管理系统-可用于大学生日常课设 功能点描述: (1)学生 学生注册为系统用户、系统用户报名助理岗位、 (2)实验室技术员 筛选报名信息、实验室助理招聘计划的...

    Spring Data JPA中文文档[1.4.3]_springdatajpa_erlang_waitxpf_

    Spring Data JPA中文文档1.4.3

    JPA教程,包括TOPLink JPA,Hibernate JPA,Open Jpa,jpa批注

    包括TOPLink JPA,Hibernate JPA,Open Jpa,jpa批注

    JPA学习中文文档

    JPA包括以下3方面的技术: RM映射元数据,JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中; JPA 的API,用来操作实体对象,执行CRUD操作,框架...

    OpenJPA 2.2.1 API (CHM格式)

    OpenJPA  OpenJPA 是 Apache 组织提供的开源项目,它实现了 EJB 3.0 中的 JPA 标准,为开发者提供功能强大、使用简单的持久化数据管理框架。OpenJPA 封装了和关系型数据库交互的操作,让开发者把注意力集中在编写...

    Apache OpenJPA 2.1 User's Guide

    OpenJPA is Apache's implementation of Sun's Java Persistence 2.0 API (JSR-317 JPA 2.0) specification for the transparent persistence of Java objects. This document provides an overview of the JPA ...

    JPA持久化简介

    Hibernate、iBATIS、TopLink、Castor JDO、Apache OJB等这么多持久层框架,你还在为学习上面那个框架而苦恼吗?你还为研究下一代是那个而头疼吗?...朋友,学习JPA吧!JPA的出现就是解决您上面的苦恼的。

Global site tag (gtag.js) - Google Analytics