2023. 02.07 17:00 복습 및 정리 시작
JPA는 데이터 타입을 크게 두 분류로 나눌 수 있다.
엔티티 타입
@Entity로 정의하는 객체다. 데이터가 변해도 식별자로 지속해서 추적이 가능하다.
회원 엔티티의 키나 나이 값을 변경해도 식별자로 인식 가능하다.
값 타입
int, Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체다.
숫자 100을 200으로 변경하면 완전히 다른 값으로 대체된다.
값 타입은 또 크게 3가지로 나눌 수 있다.
- 기본 값 타입 : 자바 기본 타입, 래퍼 클래스, String
- 임베디드 타입 : 복합 값 타입
- 컬렉션 값 타입
이제 차근 차근 알아보겠다. 우선 아래 예제 코드를 살펴보자.
@Entity
public class Member{
@Id @GeneratedValue
private Long id;
private String name;
private int age;
//주소
private String city;
private String street;
private String zipcode;
}
기본 값 타입
- 위 코드에서는 String name, int age 변수가 기본 값 타입이다.
- 생명주기를 엔티티에 의존한다.
- 값 타입은 공유하면 안 된다.
- 다른 회원 엔티티의 이름을 변경한다고 해서 나의 이름까지 변경되는 것은 잘못되었기 때문이다.
임베디드 타입
위 예제 코드에서 주소를 나타내는 필드들이 있다.
멤버가 상세한 데이터를 가지고 있는 것은 객체지향적이지 않으며 응집력만 떨어뜨린다.
이제 새로운 값 타입을 직접 정의해서 사용하는 임베디드 타입을 사용해보겠다.
@Embeddable
public class Address{
private String city;
private String street;
private String zipcode;
}
회원 엔티티 코드는 아래와 같이 수정하면 임베디드 값 타입을 사용할 수 있다.
@Entity
public class Member{
@Id @GeneratedValue
private Long id;
private String name;
private int age;
@Embedded Address homeAddress; //집 주소
}
임베디드 타입(복합 값 타입) 정리
- 새로운 값 타입을 직접 정의할 수 있다.
- 주로 기본 값 타입을 모아서 복합 값 타입이라고 한다. int, String과 같은 값 타입.
- @Embeddable : 값 타입을 정의하는 곳에 표시
- @Embedded : 값 타입을 사용하는 곳에 표시
- 기본 생성자는 필수다.
장점
- 재사용을 자주하며 높은 응집도를 가지게 된다.
- 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있다.
- 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존한다.
임베디드 타입과 테이블 매핑
- 임베디드 타입은 엔티티의 값일 뿐이다.
- 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
- 객체와 테이블을 아주 세밀하기 매핑하는 것이 가능하다.
- 잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.
- 임베디드 타입에 정의한 매핑정보를 재정의하려면 엔티티에 @AttributeOverride를 사용하면 된다.
- 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null이다.
값 타입과 불변 객체
값 타입은 복잡한 객체 사상을 조금이라도 단순화하려고 만든 개념이다.
따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.
임베디드 타입 값 타입을 여러 엔티티에서 공유하면 위험하다. 부작용이 발생할 수도 있다.
아래와 같은 예시가 있을 수 있겠다.
값 타입의 실제 인스턴스인 값을 공유하는 것은 매우 위험하다.
따라서 값을 복사해서 사용하자.
항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있지만,
문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다. 즉 주소를 참조한다.
자바 기본 타입에 값을 대입하면 값을 복사하지만, 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
따라서 객체의 공유 참조는 피할 수 없다.
불변 객체
값 타입은 부작용 걱정 없이 사용할 수 있어야 한다. 부작용이 일어나면 값 타입이라 할 수 없다.
객체를 불변하게 만들면 값을 수정할 수 없으므로 부작용을 원천 차단할 수 있다. 따라서 값 타입을 될 수 있으면 불변 객체로 설계해야 한다.
불변 객체를 구현하는 다양한 방법이 있지만 가장 간단한 방법은 생성자로만 값을 설정하고 수정자를 만들지 않으면 된다.
이러면 불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다.
Integer, String은 자바가 제공하는 대표적인 불변 객체다.
값 타입 비교
값 타입은 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야 한다.
- 동일성 비교 : 인스턴스의 참조 값을 비교, == 사용
- 동등성 비교 : 인스턴스의 값을 비교, equals() 사용
- 값 타입은 a.equals(b)를 사용해서 동등성 비교를 해야 함
- 값 타입의 equals() 메소드를 적절하게 제정의(오버 라이딩)해서 사용하자.
값 타입 컬렉션
아래는 임베디드 타입을 하나 이상 가지고 싶어서 컬렉션에 보관한 케이스다.
@ElementCollection과 @CollectionTable를 사용하면 값 타입 하나 이상을 컬렉션에 저장할 수 있다.
데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없으며, 컬렉션을 저장하기 위한 별도의 테이블이 생긴다.
값 타입 컬렉션도 지연 로딩 전략을 기본으로 사용하며, 영속성 전이와 고아 객체 제거 기능을 필수로 가진다고 볼 수 있다.
값 타입 컬렉션의 제약사항은 아래와 같다.
- 값 타입은 엔티티와 다르게 식별자 개념이 없다.
- 값은 변경하면 추적이 어렵다.
- 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고,
값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다. - 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야 한다. null 입력 X, 중복 저장 X.
이런 제약사항이 있으므로 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려해야 한다고 한다.
일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용한다.
영속성 전이 + 고아 객체 제거를 사용해서 값 타입 컬렉션처럼 사용한다.
정리
- 값 타입은 정말 값 타입이라 판단될 때만 사용해야 한다.
- 엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안 된다.
- 식별자가 필요하고, 지속해서 값을 추적, 변경해야 한다면 그것은 값 타입이 아닌 엔티티다.
참고자료
자바 ORM 표준 JPA 프로그래밍 - 김영한
자바 ORM 표준 JPA 프로그래밍 강의 - https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
2023. 02.07 17:40 복습 및 정리 마무리
댓글