有没有办法在方法上更改JPA fetch类型?

时间:2009-06-24 15:06:35

标签: jpa persistence thread-safety dao fetching-strategy

有没有办法在不编辑实体对象的情况下在单个方法上更改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 个答案:

答案 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.loadgraphjavax.persistence.fetchgraph的提示。它将检索您在图表中定义的相关实体。

您可以在此处找到“loadgraph”和“fetchgraph”之间差异的详细信息:What is the diffenece between FETCH and LOAD for Entity graph of JPA?