ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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에 대해서 잘 이해하고 사용하자. 

Designed by Tistory.