@Embedded란 무엇인가?
엔티티 내부에서 값 타입을 포함시켜 여러 필드를 하나의 객체로 묶어 사용하는 JPA 매핑 방식이다.
즉, 여러 컬럼을 하나의 객체로 묶어서 객체지향적으로 표현할 수 있도록 도와준다.
사용하는 이유
별도의 테이블을 생성하지 않고 엔티티 컬럼으로 매핑된다. 객체 지향적인 설계를 위해서 이러한 방식을 사용한다.
예를들면 아래의 코드와 같이 사용할 수 있다.
@Entity
public class User {
@Id
private Long id;
@Embedded
private Address address;
}
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
}
@Embeddable은?
여러 필드를 하나의 값 타입(Value Type)으로 묶어 엔티티에 포함시킬 수 있도록 하는 타입이다.
JPA에서 복합키를 설정하는 방법으로는 @EmbeddedId 방식과 @IdClass 방식, 총 두가지가 존재한다.
@EmbeddedId 방식은?
- 복합키에 해당하는 필드를 하나의 객체로 묶고,
해당 객체를 @EmbeddedId로 설정하여 복합키로 사용한다.
- @EmbeddedId를 사용할 때는 복합키 클래스에 반드시 @Embeddable을 선언해야 한다.
- 복합키 클래스는 equals와 hashCode를 반드시 구현해야 한다.
장점
- 복합키를 하나의 객체로 관리할 수 있다
단점
- PK 필드가 객체 내부에 존재하기 때문에, 엔티티에서 PK 필드를 바로 확인하기 어렵다
@Entity
@Table
public class Member {
@EmbeddedId
private MemberPK memberPK;
@Column(name="phone")
private String phone;
@Column(name="address")
private String address;
}
@Embeddable
public class MemberPK implements Serializable{
@Column(name="member_no")
private int memberNo;
@Column(name="member_id")
private String memberId;
public MemberPK() {
}
public MemberPK(int memberNo, String memberId) {
this.memberNo = memberNo;
this.memberId = memberId;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
MemberPK memberPK = (MemberPK) o;
return memberNo == memberPK.memberNo && Objects.equals(memberId, memberPK.memberId);
}
@Override
public int hashCode() {
return Objects.hash(memberNo, memberId);
}
}
테스트 코드를 통해 확인해보자.
@Test
public void 임베디드_아이디를_사용한_복합키_테이블_매핑_테스트() {
Member member = new Member();
member.setMemberPK(new MemberPK(1, "user01"));
member.setPhone("010-1234-5678");
member.setAddress("서울시 종로구");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(member);
transaction.commit();
/* 설명. 복합키는 하나의 타입(MemberPK)이어야 하며
* PK 비교 시 동등 비교가 가능(equals/hashCode overriding)해야 한다.
*/
Member foundMember = entityManager.find(Member.class, member.getMemberPK());
Assertions.assertEquals(member.getMemberPK(), foundMember.getMemberPK());
}
해당 코드는 @EmbeddedId를 사용한 복합키 매핑하고, 엔티티를 저장한 뒤 조회하는 테스트 코드이다.
@EmbeddedId 방식에서는 PK가 하나의 객체로 관리된다.
따라서 엔티티 조회 시에도 복합키 객체를 그대로 전달해야 한다.
그렇기 때문에 동등비교가 가능하도록 equals와 hashCode를 overriding해야한다.
@IdClass 방식은?
- 복합키에 해당하는 필드에는 각각 @Id를 설정해야 한다.
- @IdClass(MemberPK.class) 처럼 어떤 클래스가 복합키 타입인지 지정해야 한다.
- 복합키 클래스에서는 equals와 hashCode를 반드시 overriding 해야 한다.
장점
- PK 필드를 엔티티에서 바로 확인할 수 있어 가독성이 좋다.
- 복합키 객체를 거치지 않고 엔티티 필드를 바로 조회할 수 있어, JPQL 작성이 비교적 간단하다.
단점
- 복합키 필드를 엔티티와 PK 클래스 두 곳에 관리해야해서 중복 코드가 발생한다.
- 유지보수 시, 수정해야할 부분이 늘어난다 (PK 구조가 변경되면 엔티티와 PK 클래스 모두 수정해야 한다.)
@Entity
@Table
@IdClass(MemberPK.class)
public class Member {
@Id
@Column(name = "member_no")
private int memberNo;
@Id
@Column(name = "member_id")
private String memberId;
@Column(name = "phone")
private String phone;
@Column(name = "address")
private String address;
}
public class MemberPK {
private int memberNo;
private String memberId;
public MemberPK() {}
public MemberPK(int memberNo, String memberId) {
this.memberNo = memberNo;
this.memberId = memberId;
}
@Override
public int hashCode() {...}
@Override
public boolean equals(Object obj) {...}
}
테스트 코드로 확인해보자.
@Test
public void enum타입_매핑_테스트() {
Member member = new Member();
member.setMemberNo(1);
member.setMemberId("user01");
member.setPhone("010-1234-5678");
member.setAddress("서울시 서초구");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(member);
transaction.commit();
Member foundMember = entityManager.find(Member.class, new MemberPK(1, "user01"));
Assertions.assertEquals(member, foundMember);/* 동일 비교 */
}
해당 코드는 @IdClass를 이용해 복합키를 매핑하고, 엔티티를 저장한 뒤 조회하는 테스트 코드이다.
엔티티 조회 시에는 복합키 클래스(MemberPK)를 이용해
PK 값을 전달하여 조회할 수 있다.
또한 복합키 클래스는 동일한 PK인지 비교할 수 있도록
equals와 hashCode를 반드시 오버라이딩해야 한다.
.
'JPA' 카테고리의 다른 글
| [JPA] JPA의 장단점 (0) | 2026.03.15 |
|---|