有没有办法在不编辑实体对象的情况下在单个方法上更改JPA获取类型?
我有一个由JPA实体类组成的共享ORM层。该ORM层由两个DAO层访问。一个DAO需要延迟提取,因为它适用于我的Web应用程序,另一个需要急切的提取,因为我需要它是线程安全的。
这是我的线程安全DAO的一个示例方法,
@PersistenceContext(unitName = "PersistenceUnit",
type = PersistenceContextType.TRANSACTION)
private EntityManager em;
public ErrorCode findErrorCodeById(short id) {
return (ErrorCode) em.createNamedQuery("ErrorCode.findById").
setParameter("id", id).getSingleResult();
}
我如何使这个方法(或整个类)使用急切提取?
答案 0 :(得分:15)
我假设您的实体关联(@OneToOne,@ OneToMany,@ ManyToOne)是懒惰的(FetchType.Lazy)
然后我可以想到两种方式:
一个。写两个jpa查询,一个获取lazy的关联(这是hibernate的默认方式),另一个查询显式强制加载关联(参见查询中的“fetch”关键字)。
Query q = HibernateUtil.getSessionFactory().getCurrentSession() .createQuery("select c from Category as c" + " left join fetch c.categorizedItems as ci" + " join fetch ci.item as i");
B中。在检索实体后,使用Hibernate.initialize(entity)强制加载实体的延迟关系(例如通过finder ...)
ErrorCode lazyCode = findErrorCodeById(1); // eager load associations Hibernate.initialize(lazyCode);
答案 1 :(得分:9)
在JPA中,通过注释或xml映射文件在每个持久性属性上指定获取模式。
因此,实现目标的JPA供应商不可知的方法是为每个DAO层分别设置映射文件。不幸的是,这将需要为每个映射文件单独的PersistenceUnit,但您至少可以共享相同的实体类和相同的JPQL查询。
代码骨架如下。
persistence.xml:
<persistence>
<persistence-unit name="dao-eager">
<mapping-file>orm-eager.xml</mapping-file>
</persistence-unit>
<persistence-unit name="dao-lazy">
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
orm-eager.xml:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="EAGER"/>
</attributes>
</entity>
</entity-mappings>
orm-lazy.xml:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
然后,只需在DAO层中为适当的持久性单元创建EntityManagerFactory即可。
实际上你不需要两个映射文件,你可以在实体中指定LAZY或EAGER作为注释,然后在xml映射文件中指定相反的(尽管你仍然需要两个持久性单元)。
可能比上面的Hibernate解决方案多一点代码,但您的应用程序应该可以移植到其他JPA供应商。
另外,OpenJPA使用FetchGroups(从JDO借来的概念)提供与上述Hibernate解决方案类似的功能。
最后一点需要注意,FetchType.LAZY是JPA中的提示,如果需要,提供程序可能会急切地加载行。
根据请求更新。
考虑这样的实体:
@Entity
public class ErrorCode {
// . . .
@OneToMany(fetch=FetchType.EAGER) // default fetch is LAZY for Collections
private Collection myCollection;
// . . .
}
在这种情况下,你仍然需要两个持久性单元,但是你只需要orm-lazy.xml。我更改了字段名称以反映更真实的场景(默认情况下,只有集合和blob使用FetchType.LAZY)。因此生成的orm-lazy.xml可能如下所示:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<one-to-many name="myCollection" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
persistence.xml看起来像这样:
<persistence>
<persistence-unit name="dao-eager">
<!--
. . .
-->
</persistence-unit>
<persistence-unit name="dao-lazy">
<!--
. . .
-->
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
答案 2 :(得分:1)
由于没有人提到OpenJPA,我会在这里给出答案。
在OpenJPA中,可以按以下方式急切加载以前延迟配置的集合和字段
OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);
kem.getFetchPlan().addField(Order.class, "products");
TypedQuery<Order> query = kem.createQuery(yourQuery, Order.class);
参考:http://openjpa.apache.org/builds/1.0.3/apache-openjpa-1.0.3/docs/manual/ref_guide_fetch.html
答案 3 :(得分:1)
在JPA2中我使用 EntityGraphs ,它允许您定义要检索的相关实体:
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs003.htm
您可以像创建一样使用NamedQuery,并附加一个带有键javax.persistence.loadgraph
或javax.persistence.fetchgraph
的提示。它将检索您在图表中定义的相关实体。
您可以在此处找到“loadgraph”和“fetchgraph”之间差异的详细信息:What is the diffenece between FETCH and LOAD for Entity graph of JPA?