엔티티의 필드 컬렉션을 생성과 동시에 초기화를 해주는 2가지 이유가 있다.
① NPE(NullPointerException)방지
② 하이버네이트가 엔티티를 영속화 할 때 내부에서 컬렉션이 있으면 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경된다. 따라서 실제 참조가 변경될 수 있다. 그런데 나중에 개발자가 임의로 new ArrayList로 초기화하게 될 경우 이 부분이 하이버네이트가 관리하는 컬렉션에서 개발자가 직접 만든 컬렉션으로 변경될 수 있다. 그러면 하이버네이트가 정상 동작하지 않는다. 이런 문제를 방지하기 위해 필드에서 컬렉션을 생성과 초기화를 동시에 해주고, 해당 컬렉션을 바꾸지 못하도록 코드를 작성하는 것이 좋다.
하이버네이트가 내장 컬렉션으로 변경시키는 이유는 컬렉션의 데이터가 추가 되었는지 등등을 인식하고 데이터를 효율적으로 관리하기 위함이다.
하이버네티스가 내장 컬렉션을 지원하는지 증명하기
엔티티의 값 타입에 컬렉션이 있을 경우 해당 컬렉션을 하이버네티스가 내장 컬렉션으로 바꾸는지 알아보기 위해 테스트를 실행 하였다.
1. 전제 조건
먼저 Member 엔티티와 Order 엔티티를 만들고 서로 일대다, 다대일 양방향 관계로 설정하였다. 그리고 '다' 쪽에 해당하는 Order 엔티티에 외래 키를 만들었다. Member 엔티티에서 Order 엔티티로 조회할 수 있게 orders라는 컬렉션을 만들었다.
📌 Member 엔티티
@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@OneToMany(mappedBy = "member")
private List<Order> Orders = new ArrayList<>(); // 생성과 초기화을 동시에
}
📌 Order 엔티티
@Entity
@Table(name = "orders")
@Getter
@NoArgsConstructor(access = PROTECTED)
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "member_id")
private Member member;
}
2. 실행 테스트
📌 MemberTest
@Transactional
@SpringBootTest
class MemberTest {
@Autowired
private EntityManager em;
@Test
void ordersCollectionClassNameEqualTest() {
// given
Member member = new Member();
System.out.println("================== before ===================");
Class<? extends List> beforeClass = member.getOrders().getClass();
System.out.println("Orders = " + member.getOrders().getClass());
// when
em.persist(member); // 영속성 컨텍스트에 저장
System.out.println("================== after ===================");
Class<? extends List> afterClass = member.getOrders().getClass();
System.out.println("Orders = " + member.getOrders().getClass());
// then
Assertions.assertThat(beforeClass).isNotEqualTo(afterClass);
}
}
================== before ===================
Orders = class java.util.ArrayList
================== after ===================
Orders = class org.hibernate.collection.internal.PersistentBag
member 인스턴스가 em.persist(member)를 통해 영속성 컨텍스트에 저장 되었다. 영속성 컨텍스트에 저장된 후 Member 엔티티에 있는 orders 컬렉션을 호출하였더니 하이버네티스가 제공하는 내장 컬렉션으로 바뀐 것을 알 수 있다.
👀 참고 자료
https://www.inflearn.com/questions/258175
'[JPA] > JPA' 카테고리의 다른 글
[JPA] 프록시의 필드값을 조회 때는 get 방식으로 조회하자! (0) | 2022.06.07 |
---|---|
[JPA] @DataJpaTest + 테스트 DB 변경 (0) | 2022.05.15 |
[JPA] N+1 문제 (즉시 로딩 / 지연 로딩 / 일반 Join / Fetch Join) (0) | 2022.05.02 |