(请在阅读此问题后随时编辑标题)
我@ManyToOne
在实体Parent
和Child
.
Collection<Child> children
中的子项列表Parent
从未初始化,因此应该是null
.
当使用EntityManager.find(...)
for 之前持久化Parent
然后从中获取列表时,Parent
即使没有孩子,它也会给出 ArrayList,这Parent
很好。
但是,如果Parent
在同一个事务集合中持久化或合并新的子元素,null
即使持久化/合并Parent
再次使用EntityManager.find(...)
.
所以我想知道这种不同的行为,以及它是否只发生在我的环境中。
我认为它与实体的缓存有关:从缓存中找到实体并返回它而不是从 db 中获取它,并且空集合的初始化只会在从 db 中获取时发生,可能取决于 JPA 实现。
我的假设是否接近事实?如果不是,原因是什么?
下面的实体和测试用例。我的测试环境在标签中列出。
// 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;
}