20

有没有办法在不编辑实体对象的情况下更改单个方法的 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();
}

我如何让这个方法(或整个类)使用渴望获取?

4

4 回答 4

17

我假设您的实体关联(@OneToOne、@OneToMany、@ManyToOne)是惰性的(FetchType.Lazy)

然后我可以想到两种方法:

A. 编写两个 jpa 查询,一个获取惰性关联(这是休眠的默认方式),另一个查询显式强制急切加载关联(请参阅查询中的“fetch”关键字)。

        查询 q = HibernateUtil.getSessionFactory().getCurrentSession()
                .createQuery("从Category中选择c作为c" +
                        " left join fetch c.categorizedItems as ci" +
                        " join fetch ci.item as i");


B. 使用 Hibernate.initialize(entity) 在您检索到实体后强制急切加载实体的惰性关系(例如,通过 finder ...)

错误代码lazyCode = findErrorCodeById(1);
// 急切加载关联
Hibernate.initialize(lazyCode);
于 2009-06-24T18:59:05.210 回答
9

在 JPA 中,通过注释或 xml 映射文件在每个持久性属性上指定 Fetch 模式。

因此,实现目标的与 JPA 供应商无关的方法是为每个 DAO 层提供单独的映射文件。不幸的是,这将需要为每个映射文件使用单独的 PersistenceUnit,但您至少可以共享相同的实体类和相同的 JPQL 查询。

代码骨架紧随其后。

持久性.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>
于 2009-06-30T03:49:18.770 回答
2

在 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”之间差异的详细信息:JPA 的实体图的 FETCH 和 LOAD 之间的区别是什么?

于 2017-08-10T16:41:01.540 回答
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

于 2014-05-20T09:07:43.843 回答