내가 엔티티 Parent
와 Child
사이 매우 간단 @ManyToOne
양방향 매핑이왜 엔티티 초기화되지 않은 컬렉션은 현재 트랜잭션 이전에 지속 된 엔티티에 대해서만 자동으로 초기화됩니까?
(이 질문을 읽고 제목을 편집 주시기 바랍니다).
Collection<Child> children
의 Parent
에있는 자식 목록은 초기화되지 않으므로 null
이어야합니다.
이전에 대한 EntityManager.find(...)
를 사용하여 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;
}
다른 곳에서 NPE가 꽤 많이 있기 때문에 내가하는 일은 항상 좋습니다. :)하지만 우리가 openjpa가 최대 절전 모드와 동일한 작업을 수행한다고 가정하면 왜 두 가지 NPE에서 같은 방식으로 null 컬렉션을 초기화 할 수 없습니까? 작업 케이스에서와 마찬가지로 테스트 케이스? – pirho
두 테스트 케이스에서 게으른 페칭이 필요 없기 때문에 콜렉션의 컨텐츠를 변경할 이유가 없기 때문입니다. –
그리고 JPA가 실제로 동일한 프록시 부모/parent2를 반환하기 때문에 게으른 로딩을 할 이유가 없습니다. 데이터베이스에서 가져 오기를 시도하거나 (왜 느려지는지 미안 ...) 느리게하려고합니까? 이 "no reason"에 대한 자세한 내용으로 답변을 업데이트하면 해결할 수 있습니다. – pirho