이 글에서는 JPA의 데이터 타입 중 하나인 값 타입, 그 중에서도 임베디드 타입(embedded type)에 대해서 알아볼 것이다.
JPA의 데이터 타입
임베디드 타입을 알아보기에 앞서, JPA의 데이터 타입에 대해 알아보자.
JPA의 데이터 타입은 크게 두 종류로 분류할 수 있다.
- 엔티티 타입
- 값 타입
엔티티 타입
엔티티 타입의 데이터는 기본적으로 @Entity
어노테이션으로 정의한 엔티티 객체를 말하며, 데이터의 변화가 생겨도 식별자를 통해 지속적으로 변경에 대한 추적이 가능하다.
값 타입
값 타입은 int
, String
처럼 단순하게 값을 나타내는 Java의 기본 타입이나 객체를 의미한다. 식별자를 가질 수 없으며, 엔티티 타입의 데이터와 함께 존재하기 때문에 생명 주기도 엔티티 타입의 객체에 의존하게 된다. 따라서 엔티티 타입 객체의 인스턴스를 제거하면 이에 속한 값 타입 데이터들도 함께 제거된다.
또한, 값 타입에는 여러 종류가 존재한다.
- 기본 값 타입
int
,double
같은 기본 타입Integer
,Long
,Double
같은 Wrapper 타입String
- 임베디드 타입
- 값 타입 컬렉션
임베디드 타입이란?
임베디드 타입은 여러 기본 값 타입이 하나로 모인 복합 값 타입을 의미한다. 예제 코드를 통해 더 자세히 알아보자.
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
private String addressCity;
private String addressStreet;
private String zipcode;
}
위의 코드는 여러 기본 값 타입을 포함하고 있는 Member 엔티티이다. Member 엔티티의 필드 중에서 addressCity
, addressStreet
, zipcode
는 회원의 주소를 나타내는 데이터라는 것을 짐작할 수 있을 것이다. 그렇기 때문에 위와 같이 필드를 따로 두는 방식보다는 아래와 Address라는 클래스를 만들어 하나의 데이터로 나타내는 것이 더 좋을 것이다.
public class Address {
private String city;
private String street;
private String zipcode;
}
이런 상황에서 임베디드 타입으로 값 타입을 정의하면 Address 객체를 Member 엔티티의 값 타입으로 만들 수 있다.
임베디드 타입 정의
임베디드 타입을 사용하기 위해서 필요한 어노테이션이 두 가지가 있다.
@Embeddable
: 값 타입을 정의하는 곳에 표시@Embedded
: 값 타입을 사용하는 곳에 표시
이제 이 두 어노테이션을 사용하여 위에서 나온 Member 엔티티를 개선해보자. 먼저 Address 클래스를 임베디드 타입 객체로 사용하기 위해 @Embeddable
를 붙여야 한다.
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
}
그리고 Address를 값 타입으로 사용하기 위해서 다음과 같이 Member 엔티티에 있는 Address 필드에 @Embedded
어노테이션을 추가해야 한다.
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
@Embedded
private Address address;
}
임베디드 타입의 특징
임베디드 타입을 사용하기 전 알면 좋을 여러가지 특징들이 있다.
- 임베디드 타입은 엔티티의 값중 일부일 뿐이다.
- 그러므로, 임베디드 타입을 사용하기 전과 후에 매핑되는 테이블에는 변화가 없다.
- 임베디드 타입을 사용하면 객체와 테이블을 더 세밀하게 매핑하는 것이 가능하다.
- 임베디드 타입 안에 엔티티 타입의 객체를 넣어서 사용할 수 있다.
임베디드 타입 속성 재정의
만약 하나의 엔티티 안에 같은 임베디드 타입의 데이터를 두 개 이상 사용하고 싶다면, @AttributeOverride
어노테이션을 통해 임베디드 타입의 속성을 재정의하면 된다.
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@Embedded
Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="city", column=@Column(name = "COMPANY_CITY")),
@AttributeOverride(name="street", column=@Column(name = "COMPANY_STREET")),
@AttributeOverride(name="zipcode", column=@Column(name = "COMPANY_ZIPCODE"))
})
Address companyAddress;
}
임베디드 타입이 null일 경우
임베디트 타입의 데이터가 null
일 경우, 임베디드 타입에 속한 모든 필드의 값이 null
로 매핑이 된다.
member.setAddress(null);
em.persist(member);
임베디드 타입 사용 시 주의사항
임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험한 상황이 생길 수 있다.
member1.setHomeAddress(new Address("OldCity"));
Address address = member1.getHomeAddress();
address.setCity("NewCity");
member2.setHomeAddress(address);
위의 코드를 보면 member2
의 주소만 "NewCity"
로 변경되길 기대했지만, member1
의 주소도 "NewCity"
로 변경되어 버린다.
- 이런 부작용을 막으려면 임베디드 타입을 생성자로만 값을 설정하고 수정자를 만들지 않도록 하여 불변 객체로 만들어야 한다.
'JPA' 카테고리의 다른 글
[Spring / JPA] Soft Delete를 JPA에서 적용하는 방법 (0) | 2024.05.21 |
---|---|
[Spring /JPA] Spring JPA Delete Query 작성 시 주의할 점 (0) | 2023.12.26 |
[JPA] JPA Auditing (0) | 2023.12.24 |
[JPA] 지연 로딩과 즉시 로딩 (0) | 2023.12.24 |
[JPA] JPA 연관 관계 정리 (0) | 2023.12.24 |