-
[JPA] 값 타입 공유문제legacy/JPA 2023. 9. 17. 15:30
임베디드 값타입을 공유하는 두 객체가 있다. 한 객체에서 임베디드 값타입을 수정하면 이를 공유하는 다른 객체의 임베디드 값타임도 수정된다. 이는 자바에서 참조값을 수정하면 이를 참조하는 모든 객체의 값이 바뀌는 것과 동일한 원리이다.
근데 막상 임베디드 타입을 수정하니 한 객체에서만 수정되는 상황이 발생했다. 분명 공부할 때는 이를 공유하는 모든 객체의 임베디드 타입이 수정된다고 배웠는데 왜 한 객체만 수정되는 걸까? 그 이유는 ORM을 잘못 이해하고 사용했기 때문이었다.
Member 엔티티
다른 필드들은 볼 필요없이 homeAddress 필드에만 집중하면 된다.
@Entity public class Member { @Id @GeneratedValue @Column(name = "MEMBER_ID") private Long id; private String name; private int age; @Embedded @AttributeOverrides({ @AttributeOverride(name = "city", column = @Column(name = "workCity")), @AttributeOverride(name = "street", column = @Column(name = "workStreet")), @AttributeOverride(name = "zipcode", column = @Column(name = "workZipcode")) }) private Address workAddress; @Embedded private Address homeAddress; @Embedded private Period period; @Embedded private PhoneNumber phoneNumber; // Getter, Setter, 생성자 }
처음에 아래와 같이 작성했다. member1과 member2은 동일한 HomeAddess(임베디드 타입)을 공유한다. 두 객체를 영속화시키고 flush를 통해 DB에 저장했다. 이후에 두 객체를 다시 꺼내어 member2의 HomeAddress를 수정했다. 내가 기대했던 것은 member1과 member2의 HomeAddress 둘 다 바뀌기를 기대했다. 그러나 member2의 HomeAddress만 수정됐다.
Address address = new Address("city", "street", "zipcode"); Member member1 = new Member("user1", 99, null, address,null, null); em.persist(member1); Member member2 = new Member("user1", 99, null, address,null, null); em.persist(member2); em.flush(); em.clear(); Member findMember1 = em.find(Member.class, member1.getId()); Member findMember2 = em.find(Member.class, member2.getId()); findMember2.getHomeAddress().setCity("newCity");
왜 이런 상황이 일어난 것일까? 그 이유는 DB에 저장된 객체의 HomeAddress를 수정했기 때문이다. DB에 저장되기 전에는 당연히 member1과 member2 둘 다 같은 HomeAddress를 공유한다. 이때 member2의 HomeAddress를 수정했다면 member1과 member2 둘 다 HomeAddress가 수정될 것이다. 객체와 테이블을 매핑하는 ORM을 잘못 이해하고 사용했기 때문에 이러한 상황이 발생한 것이다. DB에 저장된 객체는 HomeAddress가 공유된 임베디드 타입인지 아닌지 알 수가 없다.
아래와 같이 DB에 flush되기 전에 member2의 HomeAddress를 수정하면 member1과 member2의 HomeAddress가 둘 다 수정된다.
Address address = new Address("city", "street", "zipcode"); Member member1 = new Member("user1", 99, null, address,null, null); em.persist(member1); Member member2 = new Member("user1", 99, null, address,null, null); em.persist(member2); member2.getHomeAddress().setCity("newCity"); em.flush(); em.clear();
DB에 저장되기 전후의 주소값이 다름을 알 수 있다.
Address address = new Address("city", "street", "zipcode"); Member member1 = new Member("user1", 99, null, address,null, null); em.persist(member1); Member member2 = new Member("user1", 99, null, address,null, null); em.persist(member2); System.out.println("member1.getHomeAddress().getClass() = " + System.identityHashCode(member1.getHomeAddress())); System.out.println("member2.getHomeAddress().getClass() = " + System.identityHashCode(member2.getHomeAddress())); System.out.println("=============================================================="); em.flush(); em.clear(); Member findMember1 = em.find(Member.class, member1.getId()); Member findMember2 = em.find(Member.class, member2.getId()); System.out.println("findMember1.getHomeAddress().getClass() = " + System.identityHashCode(findMember1.getHomeAddress())); System.out.println("findMember2.getHomeAddress().getClass() = " + System.identityHashCode(findMember2.getHomeAddress())); tx.commit(); // 출력 member1.getHomeAddress().getClass() = 449954659 member2.getHomeAddress().getClass() = 449954659 ============================================================== findMember1.getHomeAddress().getClass() = 541713794 findMember2.getHomeAddress().getClass() = 1179314953
결론
ORM에 대해서 잘 이해하고 사용하자.