본문 바로가기
  • 개발공부 및 일상적인 내용을 작성하는 블로그 입니다.
JPA

값 타입 - 기본 값 타입, 임베디드 타입(2)

by 방구석 대학생 2020. 10. 21.

"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."

www.inflearn.com/course/ORM-JPA-Basic#

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그

www.inflearn.com

 

 

임베디드 타입을 사용하는 방법

JPA 에서는 @Embeddable 어노테이션(값 타입을 정의하는 곳에 표시), @Embedded 어노테이션 (값 타입을 사용하는 곳에 표시) 을 만들어서 사용한다.

(기본 생성자 - default constructor 를 필수로 생성해줘야 한다.)

 

 

* 임베디드 타입의 장점?

- 재사용이 가능하다.

- 코드의 응집도가 높다.(Period, Address 와 같은 클래스의 경우 Period.isWork() 처럼 해당 값 타입만 사용하는 의미 있는 메소드를 만들어 낼 수 있다. - 상당히 객체지향적임)

- 임베디드 타입 또한 값 타입이기 때문에 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 Entity 에 생명주기를 의존한다.

 

* 임베디드 타입과 테이블 매핑

- DB 입장에서는 컬럼이 바뀔것이 없다.

- 값 타입을 쓰든 안 쓰든, 임베디드 타입을 쓰든 안 쓰든, 테이블은 똑같이 생성된다.

- 테이블 입장에서는 데이터를 잘 관리하는게 목적이기 때문에 기존 설계대로 하면 된다.

- 하지만 객체는 데이터 뿐 만 아니라 메소드와 같은 기능까지 다 들고 있다, 그렇기 때문에 묶었을 때 가져올 수 있는 이득이 많다.

 

아래의 코드를 살펴보자.

- Member.java

@Entity
public class Member{ // BaseEntity 상속 제거

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    private LocalDateTime startDate;
    private LocalDateTime endDate;

    private String city;
    private String street;
    private String zipcode;
}

 

임베디드 타입을 사용하지 않고 기존과 같이 코드를 작성하면 아래와 같은 hibernate SQL 을 얻을 수 있다.

- hibernate SQL

Hibernate: 
    
    create table Member (
       MEMBER_ID bigint not null,
        city varchar(255),
        endDate timestamp,
        startDate timestamp,
        street varchar(255),
        USERNAME varchar(255),
        zipcode varchar(255),
        TEAM_ID bigint,
        primary key (MEMBER_ID)
    )

 

여기서 비슷한 속성들을 임베디드 타입을 통해 하나로 묶어보자.

Period, Address 클래스를 생성하여 비슷한 속성들을 하나로 묶는다.

- Period.java

@Embeddable // 값 타입을 정의하는 곳에 선언하는 어노테이션
public class Period {
	//Period
	private LocalDateTime startDate;
	private LocalDateTime endDate;

	public Period() { // 기본 생성자는 필수로 있어야 한다.
	}

	public Period(LocalDateTime startDate, LocalDateTime endDate) {
		this.startDate = startDate;
		this.endDate = endDate;
	}

	// Getter, Setter
}

- Address.java

@Embeddable
public class Address {

	//Address
	private String city;
	private String street;
	private String zipcode;

	public Address() {
	}

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

	// Getter, Setter
}

 

이후 Member.java 에서 비슷한 속성끼리 묶어놓은 임베디드 타입을 사용하기 위해 아래와 같이 코드를 작성해주자.

- Member.java

@Entity
public class Member{

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @Embedded // 값 타입을 사용하는 곳에 선언하는 어노테이션
    private Period workPeriod;

    @Embedded
    private Address homeAddress;

    // Getter, Setter
}

 

이후 애플리케이션을 실행해보면 출력되는 hibernate SQL 을 통해 아래와 같이 테이블이 생성되는 것을 알 수 있다.

- hibernate SQL

Hibernate: // 임베디드 타입을 사용하기 전과 똑같은 구조로 테이블이 생성된다.
    
    create table Member (
       MEMBER_ID bigint not null,
        city varchar(255),
        street varchar(255),
        zipcode varchar(255),
        USERNAME varchar(255),
        endDate timestamp,
        startDate timestamp,
        TEAM_ID bigint,
        primary key (MEMBER_ID)
    )

 

이렇게 임베디드 타입의 클래스가 만들어지고 나면 클래스 내부의 속성들을 사용하는 메소드 들을 생성하여 응집력을 더욱 높여줄 수 있다.

 

이제 실질적으로 JpaMain.java 에서 임베디드 타입 클래스 들을 사용해보자.

- JpaMain.java

Member member = new Member();
member.setUsername("hello");
member.setHomeAddress(new Address("city", "street", "10000"));
member.setWorkPeriod(new Period());

em.persist(member);

- hibernate SQL

Hibernate: 
    /* insert hellojpa.Member
        */ insert 
        into
            Member
            (city, street, zipcode, USERNAME, endDate, startDate, MEMBER_ID) 
        values
            (?, ?, ?, ?, ?, ?, ?)

 

위의 hibernate SQL 을 보면 데이터가 정상적으로 테이블에 insert 되는 것을 확인할 수 있다.

 

 

그렇다면 왜 임베디드 타입을 활용해 주는 것이 좋을까?

- 임베디드 타입은 Entity 의 값일 뿐이다.

- 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 서로 같다.(중요한 특징)

 

* 임베디드 타입을 사용할 때와, 그렇지 않을 때의 차이점?

- 임베디드 타입을 사용하면 객체와 테이블을 아주 세밀하게(find - grained) 매핑하는 것이 가능하다.

예시 : 회원 Entity 는 이름, 근무 기간, 집 주소를 가진다 (이와 같이 추상화를 하는 방식으로 설계하면 설명하기도 좋고 모델링도 깔끔하게 딱 떨어진다.)

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

 

 

 

다음 글에선 임베디드 타입과 연관관계에 대해 알아보자.