이 글은 과거에 운영하던 블로그에서 옮겨온 글 입니다. (2022.01.15. 작성됨)
요약
하이버네이트에서는 컬렉션 타입을 org.hibernate.collection.internal.PersistentBag 인스턴스로 래핑하여 사용합니다.
이 때문에 Collection 타입에서 orphanRemoval 옵션 사용시 주의 해야합니다.
이와 관련 하여 실제 문제를 격은 사레는 다음 포스팅에서 다뤄져 있습니다.
2024.09.17 - [개발자 포포] - [Hibernate] orphanRemoval 옵션 사용시 Collection 참조를 변경하지 말자
JPA 관련 공부를 하던 중, 컬렉션 타입은 org.hibernate.collection.internal.PersistentBag 인스턴스로 래핑된다는 놀라운 사실을 알게 되었습니다.
테스트 해보기 위해 간단히 1:N 관계를 가지는 Team과 Member를 정의하고 다음과 같이 연관관계를 맺어 주었습니다.
@Entity
public class Team {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
protected Team() {
}
public Team(String name) {
this.name = name;
}
public void addMember(Member member){
members.add(member);
member.assignTeam(this);
}
...
}
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// @JoinColumn(name = "member")@ManyToOne
private Team team;
protected Member() {
}
public Member(String name) {
this.name = name;
}
public void assignTeam(Team team){
this.team=team;
}
}
다음과 같이 테스트 코드를 작성하여 members 컬렉션의 클래스 타입을 확인해 보았습니다.
@Test
void test() {
Team team = new Team("TeamA");
System.out.println("before persist -- : " + team.getMembers().getClass());
entityManager.persist(team);
System.out.println("after persist -- : " + team.getMembers().getClass());
entityManager.flush();// 영속성 컨텍스트에 있는 것을 DB에 반영. 영속성 컨텍스트를 비우진 않음.
entityManager.clear();// 영속성 컨텍스트 비움
Team findTeam = entityManager.find(Team.class, team.getId());// clear를 호출했기 때문에 db에서 다시 찾음
System.out.println("find team - member -- : " + findTeam.getMembers().getClass());
}
[결과]
영속성 상태 전에는 ArrayList 타입이었다가 영속성 상태가 되고 난 후 PersistentBag로 변경되는 것을 확인 할 수 있습니다.
before persist-- : class java.util.ArrayList
Hibernate:
insert
into
team
(id, name)
values
(null, ?)
after persist-- : class org.hibernate.collection.internal.PersistentBag
Hibernate:
select
team0_.id as id1_1_0_,
team0_.name as name2_1_0_
from
team team0_
where
team0_.id=?
find team - member-- : class org.hibernate.collection.internal.PersistentBag
하이버네이트는 컬렉션으로 참조하고 있는 대상을 추적하고 관리하기 위해 내부적으로 PersistentBag 타입 객체로 실제 인스턴스를 래핑하여 사용하게 됩니다.
PersistentBag 의 내부 구현은 다음과 같습니다. 컬렉션을 인스턴스 변수로 두고 생성자에서 이를 주입받는 것을 확인할 수 있습니다.
public class PersistentBag extends AbstractPersistentCollection implements List {
protected List bag;
...
public PersistentBag(SharedSessionContractImplementor session, Collection coll) {
super( session );
providedCollection = coll;
if ( coll instanceof List ) {
bag = (List) coll;
}
else {
bag = new ArrayList( coll );
}
setInitialized();
setDirectlyAccessible( true );
}
}
[참고]
책 - 자바 ORM 표준 JPA 프로그래밍 (저자 - 김영한)
'개발자 포포' 카테고리의 다른 글
[Hibernate] 쓰기 지연(write-behind) 동작 순서와 예외 케이스 (0) | 2025.01.05 |
---|---|
[Hibernate] Lazy Loading이 포함된 Response 유의 (2) | 2024.09.17 |
[Hibernate] orphanRemoval 옵션 사용시 Collection 참조를 변경하지 말자 (1) | 2024.09.17 |
[Spring] Spring Batch Chunk 단위 처리 결과를 ThreadLocal로 저장해보기 (0) | 2024.09.17 |
[library] XSSFWorkbook 객체 생성 시 InputStream과 File 전달 차이 (2) | 2024.09.17 |