JPA 값 타입

값 타입은 복잡한 객체를 단순하게 다루기 위한 개념으로 기본 값 타입, 임베디드 타입, 컬렉션 값 타입 세 가지가 있다.

 

기본 값 타입

엔티티에서 int, String과 같은 기본 타입과 래퍼 클래스이다.

기본 값 타입은 참조가 아니기 때문에 변경 시 깊은 복사로 값만 변경되어 추적이 불가능하다.

참조를 이용하는 값 타입은 변경 시 다른 엔티티의 필드도 바뀔 수 있어서 공유해서는 안되고, 불변 객체여야한다.

하지만 기본 값 타입은 안전하다. 

래퍼 클래스는 객체이기 때문에 공유가 가능하지만, 불변 객체이기 때문에 안전하다.

 

임베디드 타입

새로운 값 타입을 직접 정의할 수 있다 = 엔티티 클래스 내부에 다른 클래스 타입을 가진 필드를 사용할 수 있다
좌표나 주소 같은 비슷한 성격의 필드를 클래스로 분리함으로써 높은 응집도와 재사용성을 가질 수 있다.

+ 잘 설계한 ORM 애플리케이션은 매핑한 테이블 수보다 클래스가 더 많다.

 

임베디드 타입은 엔티티의 값일 뿐이기 때문에 매핑 상태에 영향을 주지 않는다.

실제 테이블 필드: [id, username, city, street, zipcode]

@Embeddable
public class Address {			//기본 생성자 필수
    private String city;
    private String street;
    private String zipCode;
}
@Entity
public class User {
    @Id @GeneratedValue
    private Long id;

    private String username;

    @Embedded
    private Address address;
}

 

임베디드 타입은 참조 객체이기 때문에 공유해서는 안된다. 하지만, 공유하지 못하도록 막을 방법이 없기 때문에 변경이 불가능한 불변 객체로 만들어야한다. 

setter를 막고, 변경 시 new를 통해서 새롭게 값을 저장해야한다. 

불변 객체이기 때문에 값을 변경할 때 새로운 인스턴스가 생성된다. 값이 같으면 같은 객체로 봐야한다.

=> equals와 hashcode를 재정의해야 한다.

(프록시 객체의 경우 각 필드가 null이기 때문에 NPE가 발생할 수 있으므로 getter를 통해서 필드 값을 채워줘야한다.)

 member.getAddress().setCity("서울"); //X  
 member.setAddress(new Address("대구")); //O

 

 

 

컬렉션 값 타입

JSON 타입이 추가되기 전 DB는 List를 저장할 수 없기 때문에 일대다의 다른 테이블을 만들어야했다. 

이때 @ElementCollection, @CollectionTable @JoinColumns를 사용해서 매핑해줄 수 있다.
컬렉션 값 타입에도 지연로딩이 적용되고, cascade = CascadeType.ALL, orpahRemoval=true를 필수로 가진다.
컬렉션 값 타입 또한 불변을 보장하기 위해 get()으로  요소를 가져와서 바꾸는 것이 아닌 remove()로 완전히 삭제 후 add()로 새로운 값을 넣어 값을 변경해야한다.
컬렉션 값 타입에 변경 사항이 발생하면, DB에서 주인 엔티티와 관련된 모든 데이터를 삭제하고, 현재 값을 모두 다시 저장하는 문제가 있다.
컬렉션 값 타입 보다 일대다 연관관계를 사용하는 것이 낫다.

'개발 > Spring' 카테고리의 다른 글

Spring JSON 응답 시 boolean 타입의 is가 생략되는 문제  (0) 2024.02.01
JPA 쿼리 방법  (0) 2024.01.28
JPA 프록시와 지연로딩  (1) 2024.01.28
JPA 영속성 전이와 고아 객체  (0) 2024.01.28
JPA 엔티티 매핑  (1) 2024.01.27