JPA의 값 타입 (김영한님 자바 ORM 표준 JPA 프로그래밍 필기 내용)
아래 후술할 여러 내용중 임베디드 타입(복합 값 타입)과, 값 타입 컬렉션에 대한 내용이 특히 중요하다고 한다.
값 타입의 분류
- JPA의 Data Type은 최상위로 볼 때 크게 Entity Type 과 Value Type으로 두가지로 분류할 수 있다. 각각 살펴보자면,
Entity Type(엔티티 타입)
- @Entity로 정의하는 객체들 (여러 테이블들과 같이)로써, 데이터가 변해도 식별자(PK 등)로 지속해 추적이 가능하다. (ex: EntityManager.find(엔티티.clsss, 엔티티.식별자()))
- 예시로 회원 Entity의 신장, 나이 값을 변경해도 이 엔티티의 식별자로 이를 인식할 수 있다.
Value Type(값 타입)
- int, Integer, String 처럼 단순히 값으로 사용하는 자바 기본타입이나 객체를 말한다.
- 값 타입은 식별자가 따로 없고 값으로만 존재하므로 JPA에서 얘를 추적할수는 없다.
이제 조금 더 알아보자면, 아래와 같다.
기본값 타입
기본적으로 기본값 타입은 생명주기는 Entity에 의존한다. 예를 들어, 회원이라는 객체를 하나 삭제하면, 그에 딸린 나이, 이름 필드도 같이 삭제가 된다.
- 자바 기본 타입(int, double 등)
- Wrapper Class (Integer, Long, Double 등)
- String
- 값 타입은 공유를 하면 안된다. 이렇게 생각해보면 쉬운데, 내가 민수라는 회원의 취미를 "밥먹기"라고 변경하려고 한다. 당연히 민수의 취미만 바꿔야지, 옆에 있는 민식이의 취미도 "밥먹기"로 변경시켜버리면 안된다. 이건 말이 어렵지 그냥 우리가 늘 코딩하며 봤던 상황이다. 예를 들어 아래와 같은 상황에서
public class Test{
public static void main(String[] args){
int a = 10;
int b = a; //b에 a의 값이 "복사"됨.
a = 20; // a의 값을 20으로 바꿔도
System.out.println("a = " + a); //얘는 20이지만
System.out.println("b = " + b); //얘는 10 그대로 있다. 같이 안바뀐다.
}
}
- 그냥 자바 자체적으로 기본타입을 다른 객체와 공유하지 않는다.
- 물론 Integer같은 래퍼클래스나, String과 같은 특수클래스는 레퍼런스를 공유가 가능해 값 자체의 공유는 가능한 놈들이지만, 다행히 자바 형아들이 변경까지 허락해주진 않았다.
아래 임베디드 타입과 컬렉션 값 타입을 사용하기 위해서는 JPA에서 따로 정의를 해주고 사용할 수 있다.
임베디드 타입(Embedded type, 복합 값 타입)
예를 들어, x,y 좌표값과 같은 것을을 관리하기 위해 Position이라는 클래스를 정의했다고 하자. 이 Position 자체를 위의 Integer, double, String 과 같이 특정 값 으로써 사용하고 싶을 때 사용하는것이 바로 임베디드 타입이라고 한다.
- 임베디드 타입은 새로운 값 타입을 직접 정의할 수 있다.
- 위에서의 Position 클래스와 같은 예시처럼 주로 기본 값 타입 들을 모아서 만들어주기 때문에 복합 값 타입이라고도 한다.
- 임베디드 타입도 int, String과 같은 값 타입 이다. Entity가 아니다. 즉, 이 임베디드 타입으로 엔티티를 추적할수가 없고, 그냥 변경하면 끝나는것.
예시로, 만약 아래와 같은 회원 엔티티가 있다고 하자.
여기서 근무 시작일~ 종료일을 근무기간으로 묶고, 도시, 번지, 우편번호 또한 간단히 클래스로 묶어 집 주소 이렇게 묶을 수 있을것이다.
이렇게 묶어주고 나면 아래 사진과 같이 쥰내 간단한 회원 엔티티가 될 수 있을 것이다.
이렇게 몇가지 기본 타입들을 묶어내서 그 자체를 하나의 타입을 쓰는게 임베디드 타입이다.
- 여기서 알 수 있는것은, 여러 중복되는 속성을 재사용해 코드를 좀 줄일 수 있고, 높은 응집도를 가진다.
- 또한 묶어낸 Period 타입 자체가 클래스인 만큼, Period.isWork() : return Boolean과 같이 해당 값 타입만 사용하는 의미있는 메소드를 만들 수 있겠다.
- 임베디드 타입 또한 값타입이므로, 이 타입의 라이프 사이클 또한 엔티티에 의존한다. 예시를 보자.
@Entity
public class Member extends BaseEntity {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USER_NAME")
private String username;
private LocalDateTime startDate;
private LocalDateTime endDate;
private String city;
private String street;
private String zipCode;
}
위와 같은 회원 엔티티에서 startDate, endDate를 묶어 Period로, city, street, zipCode를 묶어 Address라는 클래스로 따로 정의를 했다. 그 후, 아래 두 클래스는 임베디드 타입이다! 라는 명시를 JPA에게 전달해야 하기 때문에 @Embeddable 어노테이션을 붙여주고, 이를 사용하는 Member 클래스에 해당 객체들을 선언하며, 여기엔 나 임베디드 타입 쓸거다! 라는 명시를 위해 @Embedded 를 붙였다.
@Embeddable
public class Period {
private LocalDateTime startDate;
private LocalDateTime endDate;
public Period(){}
// ... 이하 getter settet 등
}
@Embeddable
public class Address {
private String city;
private String street;
private String zipCode;
public Address(){}
// ... 이하 getter settet 등
}
@Entity
public class Member extends BaseEntity {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USER_NAME")
private String username;
@Embedded
private Period period;
@Embedded
private Address address;
}
- 위 두 방식(임베디드 타입으로 쪼개버리냐, 그냥 쌩으로 Member에 다 때려박고 가냐)모두 DB 테이블의 변화는 없다. 그냥 엔티티를 좀 더 OOP 갬성으로다가 만질 수 있게되는 상큼한 것이다.
임베디드 타입과 테이블 매핑?
- 임베디드 타입은 엔티티의 값일 뿐, 뭐 별 다른 큰건 없다. 임베디드 타입을 쓰기 전과 후의 DB 테이블 구조는 동일하다.
- 대신, 객체와 테이블을 좀 더 세밀하게 매핑하는 것이 가능해져 까리하다.
- 잘 설계한 ORM 애플리케이션은 매핑한 테이블 수보다 클래스 수가 더 많다고 한다.
컬렉션 값 타입(Collection value type)
값 타입과 불변 객체
값 타입의 비교
값 타입 컬렉션
Author And Source
이 문제에 관하여(JPA의 값 타입 (김영한님 자바 ORM 표준 JPA 프로그래밍 필기 내용)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@koyoungil97/JPA의-값-타입-김영한님-자바-ORM-표준-JPA-프로그래밍-필기-내용저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)