-
값 타입JPA/JPA 기본 2023. 7. 11. 18:05
JPA의 데이터 타입 분류
엔티티 타입
- @Entity로 정의하는 객체
- 데이터가 변해도 식별자로 지속해서 추적 가능
- 예) 회원 엔티티의 키나 나이 값을 변경해도 식별자로 추적 가능
값 타입
- int, INteger, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
- 식별자가 없고 값만 있으므로 변경시 추적 불가
- 숫자 100을 200으로 변경하면 완전히 다른 값으로 대체 (변경 되면 이전 값인 100을 알 수 없음)
값 타입의 분류
1. 기본값 타입
- 자바 기본 타입(int, double)
- 래퍼 클래스(Integer, Long)
- String
2. 임베디드 타입 -> 여러 개의 값을 묶어서 사용함
3. 컬렉션 값 타임 (coolection value type)
1. 기본값 타입
예) String name, int age
생명주기를 엔티티의 의존
- 회원을 삭제하면 이름, 나이 필드도 함께 삭제
값 타입은 공유하면 X
- 회원 이름 변경시 다른 회원의 이름도 함께 변경되면 안됨
2. 임베디드 타입
- 새로운 값 타입을 직접 정의할 수 있음
- 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 함
- int, String과 같은 값 타입
회원 엔티티가 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편번호를 가진다고 가정한다.
이 때 근무 시작일, 근무 종료일은 근무로 묶어서 사용하고, 주소 도시, 주소 번지, 주소 우편번호는 주소라고 묶어서 사용할 수 있다.
이렇게 임베디드 타입으로 묶으면 회원 엔티티는 이름, 근무 기간, 집 주소를 가지게 된다.
임베디드 타입의 장점
- 재사용이 가능
- 높은 응집도
- Period.isWork()처럼 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있음
- 임베디드 타입을 포함한 모든 값 타입은 값 타입을 소유한 엔티티에 생명주기를 의존함
- 임베디드 타입도 값 타입이기 때문에 엔티티가 있을 때만 있고 엔티티가 사라지면 없어짐
Address 클래스
@Embeddable // JPA의 내장타입이기 때문에 @Embeddable @Getter public class Address { private String city; private String street; private String zipcode; protected Address() { // 기본 생성자 필수 } public Address(String city, String street, String zipcode) { this.city = city; this.street = street; this.zipcode = zipcode; } }
임베디드 타입으로 사용하려면 @Embeddable 어노테이션을 사용하여 변수들을 선언해주면 된다. 이 때 기본 생성자는 필수이다.
@Entity @Getter @Setter public class Delivery { @Embedded private Address address; }
임베디드 타입을 사용하고 싶으면 @Embedded 어노테이션 아래에 변수를 선언해주면 된다. Delivery 엔티티 안에 다른 값들이 많지만 Address 예시만 뽑아왔다.
임베디드 타입은 엔티티의 값일 뿐이고 매핑하는 테이블은 같다. 하지만 객체 테이블을 아주 세밀하게 매핑하는 것이 가능하며 잘 설계된 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.
Address address = new Address("city", "street", "10000"); Member member = new Member(); member.setName("member1"); member.setHomeAddress(address); em.persist(member); Member member2 = new Member(); member2.setName("member2"); member2.setHomeAddress(address); em.persist(member2); member.getHomeAddress().setCity("newCity");
member와 member2를 선언해주고 마지막 문장에서 member.getHomeAddress().setCity("newCity"); 를 통해 member의 city만 새롭게 바꿔줬다. 하지만 결과는 member 뿐만이 아니라 member2의 city까지 newCity로 변하게 된다.
임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함
값 타입의 실제 인스턴스인 값을 공유하는 것은 위험하기에 대신 값(인스턴스)를 복사해서 사용해야 한다.
Address address = new Address("city", "street", "10000"); Member member = new Member(); member.setName("member1"); member.setHomeAddress(address); em.persist(member); Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode()); // 이렇게 카피해서 사용해야한다. Member member2 = new Member(); member2.setName("member2"); member2.setHomeAddress(copyAddress); // 복사한 것을 여기에 넣어줌 em.persist(member2); member.getHomeAddress().setCity("newCity");
copyAddress로 카피를 해서 사용하게 된다면 첫 번째 member만 newCity가 되고, 두 번째 멤버는 원래처럼 city로 남아있게 된다.
객체 타입의 한계
항상 값을 복사해서 사용하면 공유 참조로 인해 발새어하는 부작용을 피할 수 있지만 임베디드 타임처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이기 때문에 참조 값을 직접 대입하는 것을 막을 방법이 없다.
불변 객체
- 막을 수 없기 때문에 객체 타입을 수정할 수 없게 만들어서 부작용을 원천 차단해야한다.
- 값 타입은 불변 객체로 설계해야한다.
- 불변 객체 : 생성 시점 이후 절대 값을 변경할 수 없는 객체
- 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 된다.
값 타입은 무조건 불변으로 설계하자!!!
3. 컬렉션 값 타임 (coolection value type)
컬렉션 값 타임은 다음 포스팅에서 할 예정
'JPA > JPA 기본' 카테고리의 다른 글
값 타입 - 2 (0) 2023.07.11 영속성 전이(CASCADE) (0) 2023.07.11 즉시로딩과 지연로딩 (0) 2023.07.11 프록시 (0) 2023.07.11 상속관계 매핑 (0) 2023.07.11