在 Hibernate 中(可比较的术语括在括号中),实体实例处于以下状态之一
EntityManager
API 允许您更改实体状态,换句话说,允许您加载和存储对象。如果您考虑对象状态管理,而不是 SQL 语句管理,您会发现 JPA 中的持久化更易于理解。
一旦使用通用 new
操作符创建了一个新实体实例,则该实例处于 new
状态。您可以通过将它与实体管理器关联到使它持久化
DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
em.persist(fritz);
使用实体管理器的 find()
方法,按其标识符值加载实体实例
cat = em.find(Cat.class, catId);
// You may need to wrap the primitive identifiers
long catId = 1234;
em.find( Cat.class, new Long(catId) );
在某些情况下,您并不真正希望加载对象状态,但可以引用它(即代理)。您可以使用 getReference()
方法来获取此引用。这在将子项链接到其父项而不必加载父项时特别有用。
child = new Child();
child.SetName("Henry");
Parent parent = em.getReference(Parent.class, parentId); //no query to the DB
child.setParent(parent);
em.persist(child);
em.persist(cat);
em.flush(); // force the SQL insert and triggers to run
em.refresh(cat); //re-read the state (after the trigger executes)
JP-QL 和 SQL 查询由 javax.persistence.Query
的实例表示。此接口提供用于参数绑定、结果集处理以及执行查询的方法。查询始终使用当前的实体管理器创建
List<?> cats = em.createQuery(
"select cat from Cat as cat where cat.birthdate < ?1")
.setParameter(1, date, TemporalType.DATE)
.getResultList();
List<?> mothers = em.createQuery(
"select mother from Cat as cat join cat.mother as mother where cat.name = ?1")
.setParameter(1, name)
.getResultList();
List<?> kittens = em.createQuery(
"from Cat as cat where cat.mother = ?1")
.setEntity(1, pk)
.getResultList();
Cat mother = (Cat) em.createQuery(
"select cat.mother from Cat as cat where cat = ?1")
.setParameter(1, izi)
.getSingleResult();
JPA 2 提供了更多类型安全的查询方法。真正类型安全的方法是在 第 9 章,Criteria 查询 中说明的 Criteria API。
CriteriaQuery<Cat> criteria = builder.createQuery( Cat.class );
Root<Cat> cat = criteria.from( Cat.class );
criteria.select( cat );
criteria.where( builder.lt( cat.get( Cat_.birthdate ), catDate ) );
List<Cat> cats = em.createQuery( criteria ).getResultList(); //notice no downcasting is necessary
但是,即使使用 JP-QL,您也可以受益于一些类型安全的便利性(请注意,它不像类型安全那样,编译器必须信任您返回类型。
//No downcasting since we pass the return type
List<Cat> cats = em.createQuery(
"select cat from Cat as cat where cat.birthdate < ?1", Cat.class)
.setParameter(1, date, TemporalType.DATE)
.getResultList();
我们强烈推荐使用 Criteria API 方法。虽然更冗长,但它提供编译器强制的安全(包括属性名称),当应用程序移动到维护模式时,这将得到回报。
如果使用投影,JPA 查询可以返回对象元组。每个结果元组作为对象数组返回
Iterator kittensAndMothers = sess.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.getResultList()
.iterator();
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = (Cat) tuple[0];
Cat mother = (Cat) tuple[1];
....
}
查询可以在选择子句中指定实体的特定属性,而不是实体别名。您还可以调用 SQL 聚合函数。返回的非事务性对象或聚合结果被视为“标量”结果,不是持久状态中的实体(换句话说,它们被视为“只读”)
Iterator results = em.createQuery(
"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
"group by cat.color")
.getResultList()
.iterator();
while ( results.hasNext() ) {
Object[] row = results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}
如果您需要对结果集指定范围(您要检索的最大行数和/或您要检索的第一行),请使用以下方法
Query q = em.createQuery("select cat from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.getResultList(); //return cats from the 20th position to 29th
@javax.persistence.NamedQuery(name="eg.DomesticCat.by.name.and.minimum.weight",
query="select cat from eg.DomesticCat as cat where cat.name = ?1 and cat.weight > ?2")
Query q = em.createNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
q.setString(1, name);
q.setInt(2, minWeight);
List<?> cats = q.getResultList();
Query q = em.createNamedQuery("eg.DomesticCat.by.name.and.minimum.weight", Cat.class);
q.setString(1, name);
q.setInt(2, minWeight);
List<Cat> cats = q.getResultList();
请注意,实际程序代码与使用的查询语言无关,您还可以在元数据中定义本机 SQL 查询,或通过将本机 SQL 查询放在 XML 映射文件中来使用 Hibernate 的本机工具。
您还可以调整执行查询时使用的刷新模式,以及定义用于加载实体的锁模式。
当必须保证查询执行不会触发刷新操作时,调整刷新模式会很有用。大多数情况下,您不需要关心此问题。
如果您需要将查询返回的对象锁定到特定级别,则调整锁模式会很有用。
query.setFlushMode(FlushModeType.COMMIT)
.setLockMode(LockModeType.PESSIMISTIC_READ);
Cat cat = em.find( Cat.class, new Long(69) );
cat.setName("PK");
em.flush(); // changes to cat are automatically detected and persisted
有时这种编程模型效率低下,因为它需要在同一个会话中执行 SQL SELECT(加载对象)和 SQL UPDATE(持久其更新的状态)。因此,Hibernate 提供了一种使用分离实例的替代方法。
JPA 规范通过使用 EntityManager.merge()
方法提供对使用分离实例所进行的修改的持久性来支持此开发模型
// in the first entity manager
Cat cat = firstEntityManager.find(Cat.class, catId);
Cat potentialMate = new Cat();
firstEntityManager.persist(potentialMate);
// in a higher layer of the application
cat.setMate(potentialMate);
// later, in a new entity manager
secondEntityManager.merge(cat); // update cat
secondEntityManager.merge(mate); // update mate
无论持久性上下文的何种状态,merge()
方法都将对分离实例所进行的修改合并到相应的受管实例(如果有)中。换言之,如果已经存在受管实体状态,则合并的实体状态将覆盖持久性上下文中的持久性实体状态。应用程序应该单独为可从给定的分离实例访问的分离实例merge()
,当且仅当它希望其状态也持久存在时。这可以使用瞬态持久性级联到关联实体和集合,请参见 瞬态持久性。
合并操作足够智能,能够自动检测合并分离实例是否会导致插入或更新。换言之,不必担心将新实例(而不是分离实例)传递给 merge()
,实体管理器会为您找出答案
// In the first entity manager
Cat cat = firstEntityManager.find(Cat.class, catID);
// In a higher layer of the application, detached
Cat mate = new Cat();
cat.setMate(mate);
// Later, in a new entity manager
secondEntityManager.merge(cat); // update existing state
secondEntityManager.merge(mate); // save the new instance
merge()
的用法和语义对于新用户似乎令人困惑。首先,只要您不尝试在其他新实体管理器中使用在某个实体管理器中加载的对象状态,那么您根本不需要使用 merge()
。一些应用程序将永远不会使用此方法。
实体管理器会定期执行需要的 SQL DML 语句,以同步存储中的数据和内存中持有的对象的 state。此过程称为刷新。
刷新在以下点默认发生(这是 Hibernate 的特定做法,在规范中未定义)
(异常:使用应用程序分配的标识符的实体实例将在保存时插入。)
可以更改这种默认行为,以便刷新频率降低。实体管理器的 FlushModeType
定义了两种不同的模式:只在提交时刷新或使用已解释的例程自动刷新(除非明确调用了 flush()
)。
em = emf.createEntityManager();
Transaction tx = em.getTransaction().begin();
em.setFlushMode(FlushModeType.COMMIT); // allow queries to return stale state
Cat izi = em.find(Cat.class, id);
izi.setName(iznizi);
// might return stale data
em.createQuery("from Cat as cat left outer join cat.kittens kitten").getResultList();
// change to izi is not flushed!
...
em.getTransaction().commit(); // flush occurs
在刷新期间,可能会发生异常(例如,如果某个 DML 操作违反了某个约束)。TODO:添加有关异常处理的链接。
Hibernate 提供的刷新模式比 JPA 规范中描述的刷新模式更多。特别是,对于长期会话,有 FlushMode.MANUAL
。有关详细信息,请参阅 Hibernate core 参考文档。
保存、删除或重新附加各个对象相当繁琐,尤其是在处理一组关联对象时。亲子关系就是一个典型案例。请考虑以下示例
@OneToOne(cascade=CascadeType.PERSIST)
@OneToOne(cascade= { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH } )
您甚至可以使用 CascadeType.ALL
来指定应针对特定关联级联所有操作。请记住,默认情况下不会级联任何操作。
Hibernate 提供更多本机级联选项,请参阅 Hibernate Annotations 手册以及 Hibernate 参考指南以获取更多信息。
在启用二级缓存后(请参见 第 2.2.1 节 “软件包”和 Hibernate Annotations 参考文档),Hibernate 会确保使用和正确更新缓存。但是,你可以通过传递两个属性来调整这些设置
javax.persistence.cache.retrieveMode
接受
值CacheRetrieveMode
javax.persistence.cache.storeMode
,它接受 CacheStoreMode
值
CacheRetrieveMode
控制 Hibernate 如何从二级缓存中访问信息:默认的 USE
或意味着忽略高速缓存的 BYPASS
。 CacheStoreMode
控制 Hibernate 如何将信息推送到二级缓存:默认且在从数据库中读取和写入数据库时将数据推送到高速缓存中的 USE
、不将新数据插入高速缓存(但可以使过时数据失效)的 BYPASS
以及与默认类似但即使数据已被缓存也会强制将数据推送到高速缓存中的 REFRESH
。
可以在如下位置设置这些属性
通过 setProperty
方法在特定 EntityManager
上
通过查询提示(setHint
方法)在查询中
在调用 find()
和 refresh()
并传递相应 Map
中的属性时
JPA 还引入了查询二级高速缓存和手动清除数据的 API。
Cache cache = entityManagerFactory.getCache();
if ( cache.contains(User.class, userId) ) {
//load it as we don't hit the DB
}
cache.evict(User.class, userId); //manually evict user form the second-level cache
cache.evict(User.class); //evict all users from the second-level cache
cache.evictAll(); //purge the second-level cache entirely
entityManager.get(Cat.class, catId);
...
boolean isIn = entityManager.contains(cat);
assert isIn;
还可以检查一个对象、一个关联或一个属性是否延迟的。可以独立于底层持久化提供程序来执行该操作
PersistenceUtil jpaUtil = Persistence.getPersistenceUtil();
if ( jpaUtil.isLoaded( customer.getAddress() ) {
//display address if loaded
}
if ( jpaUtil.isLoaded( customer.getOrders ) ) {
//display orders if loaded
}
if (jpaUtil.isLoaded(customer, "detailedBio") ) {
//display property detailedBio if loaded
}
但是,如果可以访问 entityManagerFactory,建议使用
PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
Customer customer = entityManager.get( Customer.class, customerId );
if ( jpaUtil.isLoaded( customer.getAddress() ) {
//display address if loaded
}
if ( jpaUtil.isLoaded( customer.getOrders ) ) {
//display orders if loaded
}
if (jpaUtil.isLoaded(customer, "detailedBio") ) {
//display property detailedBio if loaded
}
log.debug( "Customer id {}", jpaUtil.getIdentifier(customer) );
版权所有 © 2005 Red Hat Inc. 和各作者