
컴포넌트 스캔 및 리포지터리 인식애그리거트와 JPA 매핑을 위한 기본 규칙
내장 객체의 필드 중 하나의 컬럼 매핑을 재정의
- 하나의 내장 객체를 다른 이름의 컬럼으로 여러 번 재사용할 때
- 공통 속성(주소, 기간, 이름 등)을 재사용 가능한 객체로 분리하고 싶을 때
@Entity
public class Member {
@Embedded
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "work_city")),
@AttributeOverride(name = "street", column = @Column(name = "work_street")),
@AttributeOverride(name = "zipcode", column = @Column(name = "work_zipcode"))
})
private Address workAddress;
}
엔티티와 밸류에는 기본 생성자가 필요없다.
JPA에서 @Entity와 @Embeddable로 클래스를 매핑하려면 기본 생성자를 제공해야한다.엔티티가 객체로서 제 역할을 하려면 외부에 set 메서드 대신 의도가 잘 드러나는 기능을 제공해야한다.
객체가 제공할 기능 중심으로 엔티티를 구현하게끔 유도하려면 JPA 매핑 처리를 프로퍼티 방식이 아닌 필드 방식으로 선택해서 불필요한 get/set을 구현하지 말아야 한다.
리플랙션을 이용해 JPA가 직접 필드에 접근해서 값을 조작
@Entity
public class Member {
@Id // ← 필드에 위치 → JPA는 "필드 접근 방식" 사용
private Long id;
private String name;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
장점
단점
getter/setter 를 통해 JPA가 값에 접근
@Entity
public class Member {
private Long id;
@Id // ← getter 메서드에 위치 → JPA는 "프로퍼티 접근 방식" 사용
public Long getId() {
return id;
}
public void setId(Long id) { this.id = id; }
private String name;
}
장점
단점
밸류 타입의 프로퍼티를 한 개 칼럼에 매핑해야할 때

AttributeConverter로 구현
@Converter(autoApply = true)
public class MoneyConverter implements AttributeConverter<Money, Integeo {
@0verride
public Integer convertToDatabaseColumn(Money money) {
return money = = null ? null : money.getValue();
}
@0verride
public Money convertToEntityAttribute(Integer value) {
return value = = null ? null : new Money(value);
}
}
@Converter 어노테이션으로 Money 타입 적용
밸류 컬렉션 : 별도 테이블 매핑
밸류 컬렉션 : 한 개 컬럼 매핑
새로운 밸류 타입을 추가해야 한다.밸류를 이용한 ID 매핑
1세대 시스템의 주문번호와 2세대 시스템의 주문번호를 구분할 때
@Embeddable
public class OrderNo implements Serializable {
@Column(name = "order_number")
private String number;
public boolean is2ndGeneration() {
return number.startsWith("N");
}
...
is2ndGeneration() 으로 세대 구분
별도 테이블에 저장하는 밸류 매핑
애그리거트에서 루트 엔티티를 뺀 나머지 구성요소는 대부분 밸류
단지 별도 테이블에 데이터를 저장한다고 해서 엔티티인 것은 아니다. 주문 애그리거트도 OrderLine을 별도 테이블에 저장하지만 OrderLine 자체는 엔티티가 아니라 밸류이다.
자신만의 독자적인 라이프 사이클을 갖는다면 구분되는 애그리거트일 가능성이 높다.
애그리거트에 속한 객체가 밸류인지 엔티티인지 구분하는 방법은 고유 식별자를 갖는지를 확인하는 것
- 식별자를 찾을 때 매핑되는 테이블의 식별자를 애그리거트 구성요소의 식별자와 동일한 것으로 착각하면 안 된다.

ARTICLE 테이블의 데이터와 연결하기 위함이지 ARTICLE_CONTENT를 위한 별도 식별자가 필요하기 때문은 아니다.

밸류를 매핑 한 테이블을 지정하기 위해 @SecondaryTable과 @AttributeOverride 사용
name 속성은 밸류를 저장할 테이블을 지정
// @SecondaryTable로 매핑된 article_content 테이블을 조인
Article article = entityManager.find(Article.class, IL);
article_content가 불필요할 때는 대비하여 ArticleContent를 지연 로딩 방식 적용
밸류 컬렉션을 @Entity로 매핑하기
구현 기술의 한계나 팀 표준 때문에 @Entity를 사용해야하는 경우
예시 > 제품 이미지 업로드 방식에 따라 이미지 경로와 썸네일 이미지 제공 여부가 달라지는 경우

상속 구조를 갖는 밸류 타입을 사용하려면 @Entity 사용이 필요하다.
@Inheritance 애너테이션 적용
strategy 값으로 SINGLE_TABLE 사용
@DiscriminatorColumn 애너테이션을 이용하여 타입 구분용으로 사용할 칼럼 지정
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "image_type")
@Table(name = "image")
public abstract class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "image_id")
private Long id;
@Column(name = "imagej)ath")
private String path;
@Entity
@DiscriminatorValue("II")
public class Internallmage extends Image {
}
@Entity
@DiscriminatorValue("EI")
public class Externalimage extends Image {
}
Image가 @Entity 이므로 Product는 @OneToMany로 매핑
@Entity를 위한 컬렉션 객체의 clear() 메서드를 호출하면 select 쿼리로 대상 엔티티를 로딩하고, 각 개별 엔티티에 대해 delete 쿼리를 실행한다.@Embeddable 타입에 대한 컬렉션의 clear()를 메서드를 호출하면 한 번의 delete 쿼리로 삭제 처리 수행변경 빈도에 따라 어떻게 설계할지 고려 필요ID 참조와 조인 테이블을 이용한 단방향 M-N 매핑
요구사항을 구현하는 데 집합 연관을 사용하는 것이 유리하다면 id 참조를 이용한 단방향 집합 연관을 적용해 볼 수 있다.
@ElementCollection
@CollectionTable(name = "product_category",
joinColumns = @JoinColumn(name = "product_id"))
private Set<**CategoryId**> categorylds;
메모리 상에서 중복 제거 → 데이터가 많아질 때 불필요한 데이터 발생@Embeddable 매핑 타입은 함께 저장되고 삭제되므로 cascade 속성을 추가로 설정하지 않아도 된다.@Entity
@Table(name = "article")
@SecondaryTable(
name = "article_content",
pkJoinColumns = @PrimaryKeyJoinColumn(name = "id")
)
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
DIP에 따르면 @Entity, @Table은 구현 기술에 속하므로 Article과 같은 도메인 모델은 구현 기술인 JPA에 의존하지 말아야 하는데 이 코드는 도메인 모델인 Article이 영속성 구현 기술인 JPA에 의존하고 있다.
구현 기술에 대한 의존 없이 도메인을 순수하게 유지하려면?
