《Hibernate快速开始》Query /HQL and JPQL (上)

Hibernate查询语言(HQL)和Java持久性查询语言(JPQL)都是与SQL类似的面向对象模型的查询语言。JPQL是受HQL影响很大的子集。所有的JPQL查询都是有效的HQL查询,但反过来并不正确。

      HQL和JPQL都是非类型安全的方式来执行查询操作。Criteria 查询提供了一种查询类型安全的方法。有关更多信息,请参阅标准

查询API

domain 模型示例

为了更好地理解更多的HQL和JPQL示例,现在是时候熟悉本章中所有示例功能中使用的domain模型实体。


@NamedQueries({//将多个命名查询附加到相同的实体类需要将它们包装在@NamedQueries注释中
@NamedQuery(定义了一个名称为“get_person_by_name”的查询
name = "get_person_by_name",
//查询语句
query = "select p from Person p where name = :name"
)
,
@NamedQuery(
name = "get_read_only_person_by_name",
query = "select p from Person p where name = :name",
hints = {
@QueryHint(//@QueryHint 批注指定供应商特定的 JPA 查询扩展,提高查询性能,利用Hibernate的 JPA 持续性提供程序实现中的特定特性
name = "org.hibernate.readOnly",
value = "true"
)
}
)
})
@NamedStoredProcedureQueries(//使用@NamedStoredProcedureQueries注解来调用存储过程
@NamedStoredProcedureQuery(//使用@NamedStoredProcedureQuery注解并绑定到一个JPA表
name = "sp_person_phones",//JPA中的存储过程的名字
procedureName = "sp_person_phones",//存储过程的名字
parameters = {
@StoredProcedureParameter(//使用注解@StoredProcedureParameter来定义存储过程使用的IN/OUT参数
name = "personId",
type = Long.class,
mode = ParameterMode.IN
),
@StoredProcedureParameter(
name = "personPhones",
type = Class.class,
mode = ParameterMode.REF_CURSOR
)
}
)
)
@Entity//@Entity注解定义一个实体
public class Person {
@Id//@Id声明此字段为主键
@GeneratedValue//为一个实体生成一个唯一标识的主键(JPA要求每一个实体Entity,必须有且只有一个主键),@GeneratedValue提供了主键的生成策略
private String name;private String nickName;private String address;@Temporal(TemporalType.TIMESTAMP )//@Temporal注解Date格式的字段,获得”HH:MM:SS”日期格式[引申]TemporalType.TIME也会得到形如’HH:MM:SS’ 格式的日期,TemporalType.DATE 会得到形如’yyyy-MM-dd’ 格式的日期
private Date createdOn;@OneToMany(mappedBy = “person”, cascade = CascadeType.ALL)//@OneToMany用来配置一对多关联映射 在JPA中,在@OneToMany里加入mappedBy属性可以避免生成一张中间表。cascade:设置级联操作类型,CascadeType.ALL包含所有持久化方法
@OrderColumn(name = “order_id”)//@OrderColumn注解根据指定的列进行排序
private List phones = new ArrayList<>();

@ElementCollection//JPA通过@ElementCollection注解集合映射,可以自动检测元素类型
@MapKeyEnumerated(EnumType.STRING)//指定基本类型为枚举类型的映射键的枚举类型,MapKeyEnumerated注释可以应用于一个元素集合或类型的关系
private Map<AddressType, String> addresses = new HashMap<>();

@Version//JPA通过在实体类中使用@Version注解来发现数据库记录的并发操作,如果数据被修改,JPA会将这个字段自增,如果还有别的实体也在操作,它会抛出一个尝试提交的事务异常
private int version;

//Getters and setters are omitted for brevity

}

public enum AddressType {
HOME,
OFFICE
}

@Entity
public class Partner {

@Id
@GeneratedValue
private Long id;

private String name;

@Version
private int version;

//Getters and setters are omitted for brevity

}

@Entity
public class Phone {

@Id
private Long id;

@ManyToOne(fetch = FetchType.LAZY)//@ManyToOne用来配置多对一关联映射,表示在多的那一方通过延迟加载的方式加载对象(默认是非延迟加载)
private Person person;

@Column(name = “phone_number”)//@Column注解来标识实体类中属性与数据表中字段的对应关系
private String number;

@Enumerated(EnumType.STRING)
@Column(name = “phone_type”)
private PhoneType type;

@OneToMany(mappedBy = “phone”, cascade = CascadeType.ALL, orphanRemoval = true)// orphanRemoval 设置级联删除 , true代表自动删除
private List calls = new ArrayList<>( );

@OneToMany(mappedBy = “phone”)
@MapKey(name = “timestamp”)//@MapKey指定了映射,持久化timestamp或者timestamp的值
@MapKeyTemporal(TemporalType.TIMESTAMP )//@MapKeyTemporal应用于java.util.map类型的元素集合或关系
private Map<Date, Call> callHistory = new HashMap<>();

@ElementCollection
private List repairTimestamps = new ArrayList<>( );

//Getters and setters are omitted for brevity

}

public enum PhoneType {
LAND_LINE,
MOBILE;
}

@Entity
@Table(name = “phone_call”)@Table用于指明数据库的表名,若不指定则以实体类名称作为表名
public class Call {

@Id
@GeneratedValue
private Long id;

@ManyToOne
private Phone phone;

@Column(name = “call_timestamp”)
private Date timestamp;

private int duration;

//Getters and setters are omitted for brevity

}

@Entity
@Inheritance(strategy = InheritanceType.JOINED)//@Inheritance定义继承的策略,有SINGLE_TABLE、TABLE_PER_CLASS 和 JOINED 三种,JOINED 是将父类、子类分别存放在不同的表中,并且建立相应的外键,以确定相互之间的关系
public class Payment {

@Id
@GeneratedValue
private Long id;

private BigDecimal amount;

private boolean completed;

@ManyToOne
private Person person;

//Getters and setters are omitted for brevity

}

@Entity
public class CreditCardPayment extends Payment {
}

@Entity
public class WireTransferPayment extends Payment {
}

JPA查询API

在JPA中,查询是由javax.persistence.Queryjavax.persistence.TypedQueryEntityManager中获得的。创建一个内联QueryTypedQuery,你需要使用该EntityManager#createQuery方法。对于命名查询,该EntityManager#createNamedQuery方法是必需的。

 示例2.获取JPA QueryTypedQuery引用

Query query = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name"
);
TypedQuery typedQuery = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name", Person.class
);
 示例3.获取指定查询的JPA QueryTypedQuery引用

@NamedQuery(
name = "get_person_by_name",
query = "select p from Person p where name = :name"
)
Query query = entityManager.createNamedQuery( "get_person_by_name" );
TypedQuery typedQuery = entityManager.createNamedQuery(“get_person_by_name”, Person.class);
Hibernate提供了一个特定的 @NamedQuery注解,它提供了配置各种查询功能的方法,如刷新方法,缓存能力,超时间隔。

@NamedQueries({
@NamedQuery(
name = "get_phone_by_number",
query = "select p " +
"from Phone p " +
"where p.number = :number",
timeout = 1,
readOnly = true
)
})
Phone phone = entityManager
.createNamedQuery( "get_phone_by_number", Phone.class )
.setParameter( "number", "123-456-7890" )
.getSingleResult();

Query然后接口可用于控制查询的执行。例如,我们可能想要指定执行超时或控制缓存。

示例5.基本的JPA Query使用

Query query = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
// timeout - in milliseconds
.setHint( "javax.persistence.query.timeout", 2000 )
// flush only at commit time
.setFlushMode( FlushModeType.COMMIT );
有关完整的详细信息,请参阅QueryJavadocs。许多控制查询执行的设置都被定义为提示。JPA定义了一些标准提示(例如示例中的超时),但大多数提示都是基于提供者特定的环境的。依赖提供者特定的提示在一定程度上限制了你的应用程序的可移植性。


javax.persistence.query.timeout
定义查询超时,以毫秒为单位。
javax.persistence.fetchgraph
定义一个fetchgraph EntityGraph。显式指定的属性AttributeNodes被视为FetchType.EAGER(通过联合提取或随后的选择)。有关详细信息,请参阅提取中的EntityGraph讨论。
javax.persistence.loadgraph
定义一个负载图EntityGraph。显式指定为AttributeNodes的属性被视为FetchType.EAGER(通过联合提取或随后的选择)。未指定的属性视为FetchType.LAZY或FetchType.EAGER取决于元数据中属性的定义。有关详细信息,请参阅Fetching中的EntityGraph讨论。org.hibernate.cacheMode
定义CacheMode去使用
org.hibernate.query.Query#setCacheMode
org.hibernate.cacheable
定义查询是否可缓存。是真是假
org.hibernate.query.Query#setCacheable
org.hibernate.cacheRegion
对于可缓存的查询,定义要使用的特定缓存区域
org.hibernate.query.Query#setCacheRegion
org.hibernate.comment
定义应用于生成的SQL的注释
org.hibernate.query.Query#setComment
org.hibernate.fetchSize
定义要使用的JDBC提取大小
org.hibernate.query.Query#setFetchSize
org.hibernate.flushMode
定义特有的FlushMode为Hibernate来使用
请参阅org.hibernate.query.Query#setFlushMode.如果可能,请使用javax.persistence.Query#setFlushMode代替。org.hibernate.readOnly
定义由此查询加载的实体和集合应被标记为只读
看到org.hibernate.query.Query#setReadOnly
在可以执行查询之前需要发生的最后一件事是绑定任何已定义参数的值。JPA定义了一组简化的参数绑定方法。从本质上讲,它支持设置参数值(通过名称/位置)和一个专门的形式为Calendar/ Date类型另外接受一个TemporalType。
示例6. JPA名称参数绑定

Query query = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" );
// For generic temporal field types (e.g. `java.util.Date`, `java.util.Calendar`)
// we also need to provide the associated `TemporalType`
Query query = entityManager.createQuery(
“select p ” +
“from Person p ” +
“where p.createdOn > :timestamp” )
.setParameter( “timestamp”, timestamp, TemporalType.DATE );
JPQL样式的位置参数是使用问号跟一个序数 – ?1,- 来声明的?2。序号从1开始。就像命名参数一样,位置参数也可以在查询中多次出现。
示例7. JPA位置参数绑定
Query query = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like ?1" )
.setParameter( 1, "J%" );

一个好的做法是不要以混合形式出现在给定的查询中。

在执行方面,JPA Query提供了两种不同的方法来检索结果集。

  • Query#getResultList() – 执行选择查询并返回结果列表。
  • Query#getSingleResult() – 执行选择查询并返回单个结果。如果有多个结果,则抛出异常。
示例8. JPA getResultList()返回结果集
List persons = entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" )
.getResultList();
例9. JPA getSingleResult()
Person person = (Person) entityManager.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" )
.getSingleResult();

Hibernate Query  API

在Hibernate中,HQL查询表示为org.hibernate.query.Query从一个Session获得。如果一个HQL被命名为query,Session#getNamedQuery将被使用; 否则Session#createQuery需要。

例子10.获得一个Hibernate Query
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name"
);
例11.为一个命名为Query的获取Hibernate 引用
org.hibernate.query.Query query = session.getNamedQuery( "get_person_by_name" );

不仅大量的JPQL语法受到HQL语法的启发,而且许多JPA API也深受Hibernate的启发。这两份Query语法约定非常相似。

Query接口可以用来控制查询的执行。例如,我们可能想要特定的控制执行超时或控制缓存。

例子12.基本查询用法 – Hibernate

org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
// timeout - in seconds
.setTimeout( 2 )
// write to L2 caches, but do not read from them
.setCacheMode( CacheMode.REFRESH )
// assuming query cache was enabled for the SessionFactory
.setCacheable( true )
// add a comment to the generated SQL if enabled via the hibernate.use_sql_comments configuration property
.setComment( "+ INDEX(p idx_person_name)" );

有关完整的详细信息,请参阅查询 Javadocs。

这里的查询提示是数据库查询提示。根据,它们将根据Dialect#getQueryHintString直接被添加到生成的SQL中。

另一方面,JPA概念的query 提示的指向提供者(Hibernate)的提示。所以尽管他们被称为相同,但要注意他们有一个非常不同的目的。另外,请注意,Hibernate query 提示通常会使应用程序跨数据库不可移植,除非在代码添加它们之前先检查它们的方言。Flushing 在 Flushing有详细介绍。Locking 在 Locking中有详细介绍。 Persistence Contexts涵盖了只读状态的概念。
Hibernate 还允许一个应用程序通过org.hibernate.transform.ResultTransformer的约定hook进进程来建立查询结果。请参阅其Javadocs以及Hibernate提供的实现。

在我们执行查询之前需要发生的最后一件事是绑定查询中定义的任何参数的值。Query为此定义了许多重载的方法。最通用的形式是取值和Hibernate类型一样。

示例13. Hibernate名称参数绑定
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%", StringType.INSTANCE );

Hibernate通常可以推断出给定查询中上下文的参数的预期类型。在前面的例子中,因为我们在LIKE与String类型的属性进行比较时使用的参数,Hibernate会自动推断出类型; 所以上述语法可以简化。

示例14. Hibernate名称参数绑定(推断类型)
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" );

还有用于绑定常用类型的简写形式,如字符串,布尔值,整数等。

示例15. Hibernate名称参数绑定(简短形式)
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name " +
" and p.createdOn > :timestamp" )
.setParameter( "name", "J%" )
.setParameter( "timestamp", timestamp, TemporalType.TIMESTAMP);

HQL风格的位置参数遵循JDBC位置参数语法。他们宣布使用?没有以下序数。除了将相同的值绑定到每个参数之外,没有办法将两个这样的位置参数关联为“相同”。

例子16. Hibernate位置参数绑定
org.hibernate.query.Query query = session.createQuery(
"select p " +
"from Person p " +
"where p.name like ? " )
.setParameter( 0, "J%" );
此表格应被视为弃用,并且可能会在不久的将来被删除。
在执行方面,Hibernate提供了4种不同的方法。以下是2个最常用
Query#list – 执行选择查询并返回结果列表。
Query#uniqueResult – 执行选择查询并返回单个结果。如果有多个结果,则抛出异常。
例子17.休眠list()结果
List persons = session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" )
.list();

也可以从Query中提取单个结果。

例18.休眠 uniqueResult()
Person person = (Person) session.createQuery(
"select p " +
"from Person p " +
"where p.name like :name" )
.setParameter( "name", "J%" )
.uniqueResult();
如果经常使用唯一结果并且它所基于的属性是唯一的,则可能需要考虑映射自然标识并使用自然标识加载API。有关此主题的更多信息,请参阅Natural Id

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 《Hibernate快速开始》Query /HQL and JPQL (上)

  • Trackback 关闭
  • 评论 (0)
  1. 暂无评论

return top