- 참조: 자바 ORM 표준 JPA 프로그래밍
- 불변객체
값 타입을 여러 엔티티에서 공유해서 사용하면 위험하다.
public static void save(EntityManager em) {
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member1 = new Member();
member1.setId("User#1");
Address companyAddr = new Address();
companyAddr.setCity("City#1");
companyAddr.setStreet("Street#1");
member1.setCompanyAddress(companyAddr);
em.persist(member1);
Member member2 = new Member();
member2.setId("User#2");
em.persist(member2);
Address member1CompanyAddr = member1.getCompanyAddress();
member1CompanyAddr.setStreet("Street#2");
member2.setCompanyAddress(companyAddr);
tx.commit();
em.close();
}
위의 코드의 의도는 User#1 의 주소에서 Street 만 Street#2 로 변경하여 User#2 의 주소를 갱신하는것이다. 기대한 결과는 User#1 은 City#1, Street#1 의 주소를, User#2 는 City#1, Street#2 의 주소를 가진다는것이지만 조회하면 결과는 아래와 같이 나온다.
User#1과 User#2 의 Street 가 모두 갱신되어 버린다. 두 회원이 같은 주소의 인스턴스를 참조해버렸기 때문에 영속성 컨텍스트는 두 회원의 주소가 모두 변경된것으로 보고 SQL 을 수행한다.
Hibernate:
/* update
com.example.demo.test1.Member */ update
member
set
company_city=?,
company_street=?,
company_zipcode=?,
city=?,
street=?,
zipcode=?,
user_name=?
where
user_id=?
Hibernate:
/* update
com.example.demo.test1.Member */ update
member
set
company_city=?,
company_street=?,
company_zipcode=?,
city=?,
street=?,
zipcode=?,
user_name=?
where
user_id=?
위와 같은 공유참조로 인해 일어나는 버그는 원인파악이 상당히 힘들다. 이를 방지하기 위해 값 타입들은 복사하여 사용하는것이 좋다.
- 값 타입 복사
자신을 복제하는 clone 메소드를 만들어서 회원2 의 주소만 갱신해보도록 하자.
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
.............
Address member1CompanyAddr = member1.getCompanyAddress();
Address member2CompanyAddr = member1CompanyAddr.clone();
member2CompanyAddr.setStreet("Street#2");
member2.setCompanyAddress(member2CompanyAddr);
Java 에서는 객체를 할당하면 주소는 참조하는데, clone 을 하면 복제를 하므로 다른 주소값을 참조한다. 따라서 복제한 객체를 수정하여도 원본은 갱신되었다고 판단하지 않는다.
값 타입은 복사를 해서 넘기면 되겠네 라고 생각할 수 있다. 물론 맞는말이다. 코드를 사용하는 모든 사용자들이 복사를 해서 넘기면 원래 의도대로 값 타입을 사용하게 된다. 하지만 이는 불확실하며 나중에 누군가는 이를 인지하지 못할 수 있다. 보다 확실한 방법은 setter 를 제공하지 않는것이다.
- 불변 객체(Immutable Object)
위와 같은 문제를 해결하기 위해 불변 객체가 등장한다. 불변 객체를 처음들어봤다 하더라도 우리는 이미 불변객체를 사용해봤을 가능성이 크다.
우리는 Integer 를 사용할 때, Integer 를 선언해놓고 setter 를 이용해서 값을 변경하지 않는다. 대신 새로운 Integer 값을 생성한다.
불변 객체를 가장 직관적으로 구현하는 법은 setter 를 제공하지 않고 생성자로만 값을 설정할 수 있게 제공하는것이다. Address 를 불변객체로 만들려면 아래와 같이 만든다.
@Embeddable
public class Address implements Cloneable{
@Column(name = "city")
private String city;
private String street;
private String zipcode;
public Address() {
super();
}
public Address(String city, String street, String zipcode) {
super();
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
- 마치면서
이번 챕터는 JPA 의 영역이라기 보다 effective java 같은 느낌이 있다. 기초적인 내용이긴하지만 엔티티와 값 타입을 구별할 줄 아는것은 중요하다.
'Framework and Tool > JPA' 카테고리의 다른 글
JPA - 값 타입 - 요구사항 분석과 맵핑5 (0) | 2021.07.27 |
---|---|
JPA - 값 타입 - 값 타입 컬렉션 (0) | 2021.07.25 |
JPA - 값 타입 - 임베디드 타입 (0) | 2021.07.20 |
JPA - 고급맵핑 - 요구사항 분석과 맵핑4 (0) | 2021.07.18 |
JPA - 영속성 전이와 고아 객체 (0) | 2021.07.17 |
댓글