【问题标题】:Is there a way to change the JPA fetch type on a method?有没有办法更改方法上的 JPA 获取类型?
【发布时间】:2010-11-05 13:18:14
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    我假设您的实体关联(@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);

    【讨论】:

    • 感谢 Klaus,显然选项 B 会使代码依赖于导入 Hibernate,这是一个缺点,但我会尝试一下。
    • 嗨尼莫,请用绿色复选标记标记我的答案。 ;)
    • 我仍然遇到使用方法 b 的“会话关闭”问题。
    • 您是否在事务期间调用了 Hibernate.initialize?交易 tx = null;会话会话 = SessionFactoryUtil.getInstance().getCurrentSession();尝试 { tx = session.beginTransaction();错误代码lazyCode = findErrorCodeById(1); // 急切加载关联 Hibernate.initialize(lazyCode); tx.commit(); } ...
    • 克劳斯,我实际上使用的是由 Spring 管理的带注释的会话/事务。
    【解决方案2】:

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

    因此,实现目标的与 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>
    

    【讨论】:

    • Mike,您能否更详细地了解“实际上您不需要两个映射文件,您可以在实体中指定 LAZY 或 EAGER 作为注释,然后在 xml 映射中指定相反的内容文件”?
    • 我添加了一个简单的示例。希望对您有所帮助。
    • 我们将编辑 xml 以更改注释非常类似于编辑实体对象。然而,这可能是我最好的选择。
    【解决方案3】:

    在 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?

    【讨论】:

      【解决方案4】:

      由于没有人提到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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-22
        • 1970-01-01
        • 2018-07-20
        • 1970-01-01
        • 2020-02-17
        相关资源
        最近更新 更多