지나공 : 지식을 나누는 공간

JPA 7 : 임베디드 타입, JPA 데이터 타입 분류 본문

Tech/JPA

JPA 7 : 임베디드 타입, JPA 데이터 타입 분류

해리리_ 2020. 11. 9. 22:54

이번에 알아볼 내용은,

  • 기본값 타입과 값 타입의 분류
  • 임베디드 타입(복합 값 타입) - > 중요
  • 임베디드 타입의 활용
  • 임베디드 타입의 중복 해결
  • 임베디드 타입과 null

 

JPA의 데이터 타입 분류

[엔티티 타입]

  • Entity로 정의하는 객체
  • 내부 데이터가 변해도 식별자로 지속해서 추적 가능
  • 예) 회원 엔티티의 이름, 나이를 변경해도 id100번이면 id를 보고 이 엔티티를 인식할 수 있다.

[값 타입]

  • int, Integer, String 같은 단순히 값으로 사용하는 자바 기본 타입이나 객체
  • 식별자가 없이 값만 있으므로 변경되면 추적 불가능

JPA 데이터 타입 전체 분류

 

값 타입의 분류

 

임베디드 타입 : X좌표, Y좌표를 한 번에 사용하고자 묶어서 '위치'로 사용하고 싶을 때 사용.

 

컬렉션 값 타입 : 임베디드 타입이나 값 타입을 자바 컬렉션에 넣어서 사용하고 싶을 때 사용.

 

기본값 타입(primitive type) :

-> String, int 등으로 생명주기를 엔티티에 의존한다.

예) 회원을 삭제하면 회원의 이름, 나이 필드도 함께 삭제된다.

 

-> 값 타입은 공유하면 안된다. 기본 타입은 항상 값을 복사하기 때문이다.

예) 회원 이름을 변경했을 때 다른 회원의 이름도 함께 변경되면 안된다.

 

int a = 10;
int b = a;

b = 20; // b만 20으로 변하고 a는 변하지 않는다.

 

Integer같은 래퍼클래스나 String 같은 특수 클래스는 공유 가능한 객체이다.

하지만 값 변경할 수 있는 방식이 없다. 아래 setValue는 그냥 예시고, 사실은 Integer 변경 자체가 불가능하다.

 

Integer a = new Integer(10);
Integer b = a; //이건 10이 복사되어 넘어가는 게 아니라 a의 참조 레퍼런스가 넘어간다.

a.setValue(20); // 이런 거 없는 코드인데 예를 들어 이렇게 a를 20으로 변경했다고 하면
//최종적으로 a랑 b가 모두 20으로 바뀐다.
//하지만 저렇게 setValue같이 바꿀 수 있는 방법은 없다...

 

임베디드 타입과 그 활용

  • 새로운 값 타입을 직접 정의할 수 있다.
  • JPA는 임베디드 타입이라고 한다.
  • 주로 기본 값 타입을 모아서 만들기 때문에 복합 값 타입이라고도 한다.
  • int, String과 같이 값 타입이다.
  • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티의 생명주기에 의존한다.
    (엔티티 죽으면 같이 죽는 값들)

예) 회원 엔티티 안에 Long id, String name, String address가 있다.

 

  • @Embeddable : 값 타입을 정의하는 곳에 표시
  • @Embedded : 값 타입을 사용하는 곳에 표시 (기본 생성자 필수)

 

임베디드 값 타입 사용하기

private LocalDateTime startDate;
private LocalDateTime endDate;
// 위의 두 변수를 Period라는 하나의 값으로 합치고 싶을 때

 

위의 두 변수를 합쳐서 사용하고 싶다면 아래와 같은 클래스를 하나 만들고 어노테이션을 작성한다. 

글자가 흐리지만 @Embeddable이 있다.

 

@Embeddable
public class Period{

	private LocalDateTime startDate;
	private LocalDateTime endDate;
    
    //기본생성자 필수
    public Period(){
    }
}

 

이렇게 한 뒤에 다시 startDate, endDate를 선언했던 클래스로 돌아와서 @Embedded를 추가한다.

@Embeddable과 @Embedded 중 하나만 써도 되지만 둘 다 쓰기를 권장한다.

 

public class Member{

	//private LocalDateTime startDate;
	//private LocalDateTime endDate;
    
    @Embedded
    private Period period;
}

 

위처럼 쓰고 실행해보면 table create문은 endDate와 startDate로 들어간다.

 

활용은 아래와 같이 한다. Constructor가 Period 클래스에 있다고 가정한다.

Member member = new Member();
member.setPeriod(new Period("13:01:14", "15:14:11"));

 

임베디드가 좋은 이유는?

  • 임베디드 타입은 엔티티의 값일 뿐이고, 이걸 사용하기 전과 후에 매핑하는 테이블은 동일하다.
  • 장점은, 객체와 테이블을 아주 세밀하게 매핑하는 것이 가능해진다. 예) 기관값, x,y 좌표값 등
  • 잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.

 

한 엔티티에서 같은 값을 사용한다면? 예) 같은 임베디드 두 개

  • 컬럼명이 중복되는 상황이다.
Public class Member{
	
    @Embedded
    private Period period;
    
    @Embedded
    private Period period2;
}

 

이럴 땐 repeated column 이라는 에러가 발생하므로, 아래와 같은 수정이 필요하다.

Public class Member{
	
    @Embedded
    private Period period;
    
    @Embedded
    @AttributeOverrides({
    	@AttributeOverride(name="startDate", column = @Column(name = "NEW_START_DATE")),
        @AttributeOverride(name="endDate", column = @Column(name = "NEW_END_DATE")),
    })
    private Period period2;
}

 

첫 번째 객체인 period에 대해서는 이미 존재하는 필드인 start_date, end_date에 매핑되고, 두 번째 객체인 period2는 매핑할 필드를 저렇게 만들어주어야 한다. 필드는 총 4개가 생긴다. start_date, end_date, NEW_START_DATE, NEW_END_DATE.

 

임베디드 값과 NULL

  • 위의 예시에서 period 자체를 null로 해 두면 당연히 그 안의 필드인 start_date, end_date는 null로 insert된다.

 

김영한님의 자바 ORM 표준 JPA 프로그래밍 기본편을 수강하고 정리한 내용입니다.

728x90
Comments