@Embedded는 JPA(Java Persistence API)에서 객체 지향적인 데이터 모델링을 지원하기 위해 사용되는 어노테이션입니다. 이는 엔티티 클래스 내에 값 타입(Value Type) 객체를 포함(Embed)하여, 해당 객체의 속성들을 데이터베이스 테이블의 컬럼으로 매핑할 수 있게 해줍니다.
1. Embedded의 기본 개념
- Embedded(포함):
JPA 엔티티의 필드로 또 다른 객체(값 타입 객체)를 포함시킬 때 사용합니다. - 주요 어노테이션:
- @Embedded: 값 타입 객체를 엔티티에 삽입할 때 사용.
- @Embeddable: 값 타입 객체로 사용할 클래스를 정의할 때 사용.
2. Embedded와 Embeddable의 관계
@Embedded와 @Embeddable은 항상 같이 사용됩니다.
@Embeddable
- 값 타입 객체로 사용할 클래스를 정의할 때 사용.
- 독립적인 엔티티가 아니며, 다른 엔티티에 포함됩니다.
- 데이터베이스 테이블에서는 포함된 엔티티의 필드들이 그대로 컬럼으로 매핑됩니다.
@Embedded
- 엔티티 클래스의 필드에 @Embeddable로 정의된 클래스를 삽입할 때 사용.
- 기본적으로 @Embedded는 생략 가능(암시적으로 동작).
3. Embedded의 동작 원리
@Embedded로 매핑된 값 타입의 필드들은 해당 필드에 포함된 객체의 모든 속성이 엔티티의 컬럼으로 분해되어 데이터베이스에 저장됩니다. 이를 통해 엔티티 클래스가 더 구조적이고 모듈화될 수 있습니다.
예제
1. Address라는 값 타입 정의
import jakarta.persistence.Embeddable;
@Embeddable
public class 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
}
2. User 엔티티에서 Address 포함
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Embedded;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address address;
// Getter, Setter
}
3. 결과적으로 매핑되는 테이블
User 엔티티는 아래와 같이 테이블로 변환됩니다.
id | name | city | street | zipCode |
1 | John Doe | Seoul | Gangnam | 12345 |
2 | Jane Smith | New York | Manhattan | 54321 |
4. Embedded의 활용 이유
- 객체지향적인 설계
- 데이터베이스 테이블에 컬럼을 나열하는 대신, 객체를 이용해 관련 데이터를 묶어 구조적으로 표현할 수 있습니다.
- 재사용성
- @Embeddable로 만든 값 타입 클래스는 여러 엔티티에서 재사용할 수 있습니다.
- 예를 들어, Address, ContactInfo 같은 공통 정보를 재사용 가능.
- 가독성 및 유지보수성
- 엔티티 클래스에서 반복적으로 사용되는 속성을 제거하고, 이를 별도의 값 타입 객체로 정의해 코드 가독성을 높입니다.
- 캡슐화
- 값 타입으로 묶어서 관련 속성을 하나의 객체로 관리할 수 있어 응집도가 높아집니다.
5. Embedded의 활용 예제
5.1. 단일 값 타입 매핑
Employee 엔티티와 ContactInfo 값 타입
@Embeddable
public class ContactInfo {
private String email;
private String phone;
// 기본 생성자
public ContactInfo() {}
public ContactInfo(String email, String phone) {
this.email = email;
this.phone = phone;
}
// Getter, Setter
}
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private ContactInfo contactInfo;
// Getter, Setter
}
5.2. 복합 값 타입 매핑
동일한 값 타입을 여러 필드로 사용
@Entity
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "office_city")),
@AttributeOverride(name = "street", column = @Column(name = "office_street")),
@AttributeOverride(name = "zipcode", column = @Column(name = "office_zipcode"))
})
private Address officeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "home_city")),
@AttributeOverride(name = "street", column = @Column(name = "home_street")),
@AttributeOverride(name = "zipcode", column = @Column(name = "home_zipcode"))
})
private Address homeAddress;
// Getter, Setter
}
6. Embedded의 주의사항
- 값 타입은 불변 객체로 설계
- 값 타입은 공유 참조로 인해 여러 엔티티에서 값이 변경될 위험이 있습니다.
- 따라서 Setter를 제거하고, 생성자만을 통해 초기화하도록 설계하는 것이 좋습니다.
- 테이블 컬럼 이름 충돌 가능성
- 동일한 값 타입을 여러 필드에서 사용할 경우 컬럼 이름이 충돌할 수 있습니다. 이를 해결하려면 @AttributeOverrides를 사용해야 합니다.
- 별도 테이블 생성 불가
- @Embeddable은 독립적인 테이블로 매핑되지 않으며, 항상 포함된 엔티티의 테이블에 속합니다.
- 직렬화 필요
- @Embeddable 클래스는 직렬화 가능(Serializable)하도록 설계하는 것이 일반적입니다.
7. 결론
- JPA의 **@Embedded**는 객체지향적 설계를 데이터베이스에 잘 매핑하기 위한 강력한 도구입니다.
- 재사용성, 가독성, 유지보수성을 높이고, 공통 데이터를 처리하거나 복잡한 엔티티 설계를 단순화하는 데 유용합니다.
- 그러나 설계 시 불변 객체로 구현하고, 컬럼 충돌 문제를 주의해야 합니다.
'Spring > Spring JPA' 카테고리의 다른 글
배치 쿼리의 성능 이슈 원인 간단하게 (0) | 2025.01.15 |
---|---|
영속성 컨텍스트(Persistence Context) (0) | 2025.01.07 |
@Converter 간단하게 (1) | 2025.01.07 |
Native Query (1) | 2025.01.06 |
JPQL 간단히 (0) | 2025.01.06 |