为什么实体未初始化集合仅在当前事务之前持久保存的实体中自动初始化? [英] Why Entitys uninitialized collection is initialized automatically only for Entities persisted before current transaction?

查看:164
本文介绍了为什么实体未初始化集合仅在当前事务之前持久保存的实体中自动初始化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(请在阅读此问题后随时编辑标题)

(Please feel free to edit the title after reading this question)

我很简单 @ManyToOne 实体之间的双向映射

I have quite simple @ManyToOne bidirectional mapping between entities Parent and Child.

儿童名单收集<儿童> 中的子项永远不会被初始化,所以它应该是 null

使用 EntityManager.find(...)以前持久然后从那个 Parent 获取列表给出了ArrayList,即使没有这个 Parent 的子项,也没关系。

When using EntityManager.find(...) for previously persisted Parent and then getting the list from that Parent gives ArrayList even there are no children yet with this Parent and it is fine.

但是,如果在同一个孩子的交易集合中持久或合并新的 Parent ,则 null 即使使用 EntityManager.find(...) Parent c $ c>。

However if persisting or merging a new Parent in the same transaction collection of children will be null even if the persisted/merged Parent is fetched again with EntityManager.find(...).

所以我想知道这种不同的行为以及它是否仅在我的环境中发生。

So i wonder this different behavior and if it is happening only in my environment.

我认为它与实体的缓存有关:实体是从缓存中找到的,它返回而不是从db中获取实例,并且只有从db中获取时才会发生空集合的初始化,可能取决于JPA实现。

I assume it has something to do with the caching of entities: entity is found from cache and it is returned instead of fetching it from db AND the initialization of empty collections will happen only when fetched from db, maybe depending on the JPA implementation.

我的假设是否接近事实,如果不是,原因是什么?

Is my assumption even near the truth and if not what is the reason ?

下面的实体和测试用例。我的测试环境列在标签中。

Entities and test cases below. My test environment listed in tags.

// using lombok
@Slf4j
@RunWith(Arquillian.class)
public class NoPersistTest {

    @PersistenceContext
    private EntityManager em;

    @Deployment
    public static final WebArchive deploy() {
        WebArchive wa = ShrinkWrap.create(WebArchive.class, "test.war")
                .addAsWebInfResource("test-persistence.xml", "persistence.xml").addClasses(Parent.class, Child.class);
        return wa;
    }

    @Test
    @Transactional
    public void testWithPreviouslyPersistedParent() {
        Parent parent = em.find(Parent.class, 1); // has no children in db
                                                    // before
        Child child = new Child();
        child.setParent(parent);
        parent.getChildren().add(child);
        log.info("type of Collection<Child> is {}", parent.getChildren().getClass().getName());
        // above logs "type of Collection<Child> is
        // org.apache.openjpa.util.java$util$ArrayList$proxy"
    }

    @Test(expected = NullPointerException.class)
    @Transactional
    public void testPersistingParentInSameTransaction() {
        Parent parent = new Parent();
        em.persist(parent);
        Parent parent2 = em.find(Parent.class, parent.getId());
        Child child = new Child();
        child.setParent(parent2);
        log.info("Collection<Child> is {}", parent2.getChildren());
        // above logs Collection<Child> is null
        parent2.getChildren().add(child);
    }

    @Test(expected = NullPointerException.class)
    @Transactional
    public void testMergingParentInSameTransaction() {
        Parent parent = new Parent();
        parent = em.merge(parent);
        Parent parent2 = em.find(Parent.class, parent.getId());
        Child child = new Child();
        child.setParent(parent2);
        log.info("Collection<Child> is {}", parent2.getChildren());
        // logs Collection<Child> is null
        parent2.getChildren().add(child);
    }

}

@Entity @Getter @Setter
public class Parent {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
    private Collection<Child> children;

    private Date created = new Date(); // just to have something to persist

}

@Entity @Getter @Setter
public class Child {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private Date created = new Date(); // just to have something to persist

    @ManyToOne(optional=false)
    private Parent parent;


}


推荐答案

下面的答案是正确的,我只是想在其他地方的评论中添加更多信息。

The answer below is correct, I'd just like to add some more information as I was asked to in a comment elsewhere.

JPA使用缓存来避免数据库命中在可能的情况下,以及仍然需要数据库命中的地方,缓存可以避免重建对象的成本并允许维护身份 - 确保在遍历A-> B-> A循环引用时返回相同的A实例。

JPA uses caching to avoid database hits where possible, and where a database hit is still required, caching avoids the cost of rebuilding objects and allows maintaining Identity - ensuring you get back the same A instance when traversing A->B->A circular references.

当您持久化实体时,您将其作为托管实体放置在EntityManager缓存中 - 在该EntityManager上调用find将返回您刚刚传入的完全相同的实例。

When you persist an entity, you are placing it in the EntityManager cache as a managed entity - calling find on that EntityManager will return you the same exact instance you just passed in.

  A initialA = new A();
  A managedA = em.persist(initialA);
  managedA==initialA

持久呼叫本身不会改变您实体内的任何内容(除了可能是ID(如果允许使用预分配的序列),所以任何空引用仍然是null。

The persist call itself will not change anything within your entity (except possibly the ID if a sequence that allows preallocation to be used), so any null references will still be null.

最终事务提交并且取决于您的提供者,实体可以缓存在二级缓存中。我假设你不是为了简洁而使用它;除非你强制EM刷新这个实例(如果它是一个新的!先刷新!)或者在一个单独的EntityManager中读取它,你将总是使用任何空引用获得相同的实例。

Eventually the transaction commits and depending on your provider, entities can be cached in a second level cache. I'll assume you aren't using it for the sake of brevity; unless you force the EM to refresh this instance (flush first if its a new one!) or read it in a separate EntityManager, you will always get that same instance back with any null references.

如果您刷新它或以其他方式重新加载它,您的JPA提供程序需要根据您的映射设置对象中的所有内容,就像它在数据库中一样。由于null不是集合映射的可持久状态,这意味着它将急切地获取您的引用,或者将代理放在那里以实现惰性关系,从而导致您找到一个空集合。

If you refresh it or otherwise cause it to be reloaded, your JPA provider is required to set everything in the object as it is in the database, according to your mappings. Since null isn't a persistable state for a collection mapping, that means it will either eagerly fetch your references, or place proxies in there for lazy relationships, causing you to find an empty collection.

这篇关于为什么实体未初始化集合仅在当前事务之前持久保存的实体中自动初始化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆