본문 바로가기

JPA/값 타입

임베디드 타입

2021.08.28 - [JPA] - 값 타입

 

값 타입

JPA 데이터 타입의 분류 JPA에는 크게 엔티티 타입과 값 타입이라는 두 개의 타입이 존재한다. 무엇인지 알아보자! 1. 엔티티타입 @Entity로 정의하는 클래스는 엔티티 타입으로 데이터가 변해도 식

code-mania.tistory.com

이번 글에서는 임베디드 타입이란 무엇인지 알아보겠다.
혹시 값 타입이 무엇인지 잘 모른다면 별 거 없으니 위 글을 먼저 읽어보는 것을 권장한다!


임베디드 타입

임베디드 타입은 사용자가 직접 정의해야 한다.
주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 한다.
임베디드 타입을 실제로 사용해보면서 어떤 것인지 알아보겠다!

학생 엔티티는 이름, 나이, 입학날짜, 졸업날짜, 사는 곳의 도시, 도로명, 우편번호를 가진다고 하자!
여기서 임베디드 타입을 사용하면, 필요에 따라서 필드들을 묶을 수 있다.
묶어보면 다음과 같다.
⇒ 학생 엔티티는 이름, 재학기간(입학 및 졸업날짜), 집주소(사는 도시, 도로명, 우편번호)를 가진다.
이를 클래스 다이어그램으로 살펴보면 다음과 같다!

임베디드 타입을 사용한 Student Entity 

Student 클래스가 Period와 Address 타입의 필드를 가지고 있는 것을 확인할 수 있다.
실제 코드를 통해 어떻게 임베디드 타입을 사용하는지 알아보자!


임베디드 타입 코드로 알아보기

Address.java

package hellojpa;

import lombok.Getter;

import javax.persistence.Embeddable;

@Getter
@Embeddable
public class Address {

    private String city;

    private String street;

    private String zipcode;

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    public Address() {
    }
}

 

Period.java

package hellojpa;

import lombok.Data;

import javax.persistence.Embeddable;
import java.time.LocalDateTime;

@Data
@Embeddable
public class Period {
    private LocalDateTime admissionDate;
    private LocalDateTime graduationDate;
}


Student.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;

@Entity
@Data
public class Student {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int age;
    
    @Embedded
    private Period period;

    @Embedded
    private Address address;
}

 

가장 눈에 띄는 것은 @Embedded와 @Embeddable이라는 어노테이션이다.
@Embeddable은 값 타입으로 사용할 클래스에,
@Embedded는 값 타입을 사용하는 곳에 표기해주면 된다.
(두 어노테이션 중 하나만 표기해도 동작은 정상적으로 되지만, 가독성을 위해 두 어노테이션 모두 다 표기하는 것을 추천한다.)
또한 임베디드 클래스에는 기본 생성자가 필수이며,
임베디드 클래스의 필드에는 @Column과 같이,
엔티티의 필드에 사용가능한 어노테이션들을 그대로 적용할 수 있다.

 

임베디드의 특징
- 재사용이 가능하다(여러 엔티티에서 사용할 수 있다)
- 해당 임베디드 타입만 사용하는 의미 있는 메서드를 만들 수 있다. => 응집도가 높아진다.
   ex) Period.isInSchool(): 재학 중인지 아닌지 판별하는 메서드
- 임베디드 타입을 포함한 모든 값 타입은 자신을 소유한 엔티티에 생명주기를 의존한다.
- 임베디드 타입의 사용유무는 테이블에 영향을 끼치지 않는다.(클래스 구조만 변함)
- 임베디드 타입의 객체가 null이면 해당 임베디드 타입과 매핑된 컬럼 값은 모두 null이 된다.

 

마지막으로 임베디드를 같은 클래스 내에서 2번 사용하고싶을 때는 어떻게 해야하는지와
실제 Student Entity를 DB에 저장하는 코드를 살펴보고 마치겠다!

Student Entity에서 학생의 집 주소뿐만 아니라, 학교 주소를 저장해야 해서 Address를 2번 사용해야 한다고 가정해보겠다!
(물론 조금 많이 억지다.... 학교 테이블을 따로 만드는 게 무조건 맞다...)

 

Student.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;

@Entity
@Data
public class Student {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int age;

    @Embedded
    private Period period;

    @Embedded
    private Address address;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "city", @Column(name = "SCHOLLCITY")),
            @AttributeOverride(name = "street", @Column(name = "SCHOOLSTREET")),
            @AttributeOverride(name = "zipcode", @Column(name = "SCHOOLZIPCODE"))
    })
    private Address schollAddress;
}

 

같은 임베디드 타입 클래스를 두 번 사용할 경우 @AttributeOverrides와 @AttributeOverride 어노테이션을 이용해서,
각 필드에 매핑되는 컬럼명을 지정해줘야 한다.
그렇지 않으면 컬럼이름이 반복되어서 에러가 발생한다. 사용법은 간단하다!
@AttributeOverrides의 매개변수로 @AttributeOverride의 배열을 전달하면 된다.
또한 @AttributeOverride의 매개변수로 필드명, 매핑될 컬럼명을 넣어주면 된다!

 

JpaMain.java - student 저장 로직

Address address = new Address("인천광역시", "부평대로", "100");
Period period = new Period();
period.setAdmissionDate(LocalDateTime.now());
period.setGraduationDate(null);

Student codeMania = new Student();
codeMania.setName("code-mania");
codeMania.setAddress(address);
codeMania.setPeriod(period);
em.persist(codeMania);

tx.commit();

DB 결과

 

저장 로직은 딱히 어려울 것이 없으며,
DB에 값도 잘 들어가는 것을 확인할 수 있다.

'JPA > 값 타입' 카테고리의 다른 글

값 타입 컬렉션을 엔티티로 승격시키기  (0) 2021.09.06
값 타입 컬렉션  (0) 2021.09.04
값 타입의 비교  (0) 2021.08.31
임베디드 타입과 불변 객체  (0) 2021.08.28
값 타입  (0) 2021.08.28