jpa的使用和详解

最近在公司频繁地使用jpa ,也阅读很多更大网站的文章,现在我把阅读过的文章总结成一篇

Bean Validation是JavaEE6数据验证新框架,ValidationAPI并不依赖特定的应用层或是编程模型,这样同一套验证可由应用的所有层共享.它还提供了通过扩展ValidationAPI来增加客户化验证约束的机制以及查询约束元数据仓库的手段. 

  在Java EE6的BeanValidation出现之前,开发者不得不在表示层框架、业务层以及持久层中编写验证规则以保证这些规则的同步性,但这么做非常浪费时间而且极易出错.BeanValidation是通过约束实现的,这些约束以注解的形式出现,注解可以放在JavaBean(如backingbean)的属性、方法或是类上面.约束既可以是内建的注解(位于javax.validation.constraints包下面),也可以由用户定义。一些常用的内建注解列举如下: 

   ◆Min:被@Min所注解的元素必须是个数字,其值要大于或等于给定的最小值。
  ◆Max:被@Max所注解的元素必须是个数字,其值要小于或等于给定的最大值。 
  ◆Size:@Size表示被注解的元素必须位于给定的最小值和最大值之间。支持Size验证的数据类型有String、Collection(计算集合的大小)、Map以及数组。 
  ◆NotNull:@NotNull确保被注解的元素不能为null。 
  ◆Null:@Null确保被注解的元素一定为null。 
  ◆Pattern:@Pattern确保被注解的元素(String)一定会匹配给定的Java正则表达式。  

Hibernate4之JPA规范配置详解

@Table 

Table用来定义entity主表的name,catalog,schema等属性。 
属性说明: 
  • name:表名
  • catalog:对应关系数据库中的catalog
  • schema:对应关系数据库中的schema
  • UniqueConstraints:定义一个UniqueConstraint数组,指定需要建唯一约束的列.UniqueConstraint定义在Table或SecondaryTable元数据里,用来指定建表时需要建唯一约束的列。下面是指定2个字段要唯一约束.
Example:
    @Entity
    @Table(
        name="EMPLOYEE",
        uniqueConstraints=
            @UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})
    )
    public class Employee { ... }

@ID 和 @GeneratedValue

通过annotation来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 
其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法, 
JPA提供四种标准用法,由@GeneratedValue的源代码可以明显看出.

@Target({METHOD, FIELD})
@Retention(RUNTIME)

public @interface GeneratedValue {

    GenerationType strategy() default AUTO;

    String generator() default "";
}
其中GenerationType: 
package javax.persistence;

public enum GenerationType {

    TABLE,

    SEQUENCE,

    IDENTITY,

    AUTO
}
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO. 
  • TABLE:使用一个特定的数据库表格来保存主键。 
  • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。 
  • IDENTITY:主键由数据库自动生成(主要是自动增长型) 
  • AUTO:主键由程序控制(也是默认的,在指定主键时,如果不指定主键生成策略,默认为AUTO)
@Id
    @GeneratedValue
    private Long id;

四种数据库的支持情况如下:

数据库名称

支持的id策略

mysql

GenerationType.TABLE
GenerationType.AUTO
GenerationType.IDENTITY
不支持GenerationType.SEQUENCE

oracle

strategy=GenerationType.AUTO
GenerationType.SEQUENCE
GenerationType.TABLE
不支持GenerationType.IDENTITY

postgreSQL

GenerationType.TABLE
GenerationType.AUTO
GenerationType.IDENTITY
GenerationType.SEQUENCE
都支持

kingbase

GenerationType.TABLE
GenerationType.SEQUENCE
GenerationType.IDENTITY
GenerationType.AUTO
都支持

@GeneratedValue:主键的产生策略,通过strategy属性指定。

jpa的使用和详解_第1张图片

  主键产生策略通过GenerationType来指定。GenerationType是一个枚举,它定义了主键产生策略的类型。

  1、AUTO 自动选择一个最适合底层数据库的主键生成策略。如MySQL会自动对应auto increment。这个是默认选项,即如果只写@GeneratedValue,等价于@GeneratedValue(strategy=GenerationType.AUTO)。

  2、IDENTITY 表自增长字段,Oracle不支持这种方式。

  3、SEQUENCE 通过序列产生主键,MySQL不支持这种方式。

  4、TABLE 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。不同的JPA实现商生成的表名是不同的,如 OpenJPA生成openjpa_sequence_table表,Hibernate生成一个hibernate_sequences表,而TopLink则生成sequence表。这些表都具有一个序列名和对应值两个字段,如SEQ_NAME和SEQ_COUNT。

  在我们的应用中,一般选用@GeneratedValue(strategy=GenerationType.AUTO)这种方式,自动选择主键生成策略,以适应不同的数据库移植。

  如果使用Hibernate对JPA的实现,可以使用Hibernate对主键生成策略的扩展,通过Hibernate的@GenericGenerator实现。

  @GenericGenerator(name = "system-uuid", strategy = "uuid") 声明一个策略通用生成器,name为"system-uuid",策略strategy为"uuid"。

  @GeneratedValue(generator = "system-uuid") 用generator属性指定要使用的策略生成器。

  这是我在项目中使用的一种方式,生成32位的字符串,是唯一的值。最通用的,适用于所有数据库。



构建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 criteriaQuery = criteriaBuilder.createQuery(Employee.class);

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

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

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 typedQuery = em.createQuery(criteriaQuery);
List result = typedQuery.getResultList();

元模型实例通过调用 EntityManager.getMetamodel 方法获得,EntityType的元模型实例通过调用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_和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 exp = root.get(Employee.id);
//
List 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类型(也可以是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 d = cb.createQuery(Dept.class);
Root deptRoot = d.from(Dept.class);
deptRoot.fetch("employeeCollection", JoinType.LEFT);
d.select(deptRoot);
List 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 criteriaQuery = criteriaBuilder.createQuery(String.class);
Root root = criteriaQuery.from(Dept.class);
criteriaQuery.select(root.get(Dept_.name)); 

参数化表达式

     在JPQL中,查询参数是在运行时通过使用命名参数语法(冒号加变量,如 :age)传入的。在Criteria查询中,查询参数是在运行时创建ParameterExpression对象并为在查询前调用TypeQuery,setParameter方法设置而传入的。下面代码片段展示了类型为Integer的ParameterExpression age,它被设置为24:
ParameterExpression age = criteriaBuilder.parameter(Integer.class);
Predicate condition = criteriaBuilder.gt(testEmp.get(Employee_.age), age);
criteriaQuery.where(condition);
TypedQuery testQuery = em.createQuery(criteriaQuery);
List 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 criteriaQuery = criteriaBuilder .createQuery(Employee.class);
 Root 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 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 criteriaQuery = criteriaBuilder.createQuery(EmployeeDetails.class);
  Root 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"white-space: normal;"

返回Object[]的查询

Criteria查询也能通过设置值给CriteriaBuilder.array方法返回Object[]的结果。下面的代码片段中,数组大小是2(由String和Integer组成)。
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Object[].class);
  Root 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 criteriaQuery = criteriaBuilder.createTupleQuery();
   Root 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

jpa的hql查询

JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
 
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")
@Entity
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" )
})
@Entity
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 porder by p.age desc, p.birthday asc ");
 
查询部分属性
通常来说,都是针对 Entity类的查询,返回的也是被查询的Entity类的实体。JP 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 Integer personid;
privateStringname;
   。。。。
public SimplePerson() {
}
public SimplePerson(Integer personid, String name) {
this . name = name;
this . personid = 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 支持的聚合函数 包括:
1. AVG()
2. SUM()
3. COUNT() 返回类型为 Long ,注意 count(*) 语法在 hibernate 中可用,但在 toplink 其它产品中并不可用
4. MAX()
5. MIN()
 
例:
// 获取最大年龄
Query query = em.createQuery("selectmax(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 oinner joino.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 list =order.getOrderItems();
Iterator 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 list = order.getOrderItems(); 才会执行一条 SQL 语句来加载属于当前OrderOrderItems ,编译成的 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条语句获得每个OrderorderItems 集合属性。为了避免N+1的性能问题,我们可以利用join fetch一次过用一条SQL语句把Order的所有信息查询出来
 
例子
// 获取 26 岁人的订单 ,Order 中必须要有 OrderItem
Query query = em.createQuery("select o from Order oinner join fetcho.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 DISTINCTo 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 whereo.ower =?1order 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 wherenot(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 andnot 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
 
 
子查询
子查询可以用于 WHEREHAVING条件语句中
例:
// 查询年龄为 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) {
try {
int index = (whichpage-1) * max;
Query query =em.createQuery("from Person p order by personid asc");
List list = query.setMaxResults(max).
setFirstResult(index) .
getResultList();
em .clear(); // 分离内存中受EntityManager管理的实体bean,让VM进行垃圾回收
return list;
}

jpa的sql查询



你可能感兴趣的:(jpa的使用和详解)