[Basic-6] 고급 관계 매핑

2026. 1. 6. 12:48·Spring/JPA

1. 상속 관계 매핑

1.1. 관계형 데이터베이스와 객체의 상속 차이

관계형 데이터베이스는 상속이라는 개념이 존재하지 않는다. 대신 데이터베이스 설계에서 슈퍼타입-서브타입 모델링이라는 기법이 객체의 상속과 유사한 역할을 한다.

객체의 상속

// 객체의 상속 구조
class Item {
    private Long id;
    private String name;
    private int price;
}

class Album extends Item {
    private String artist;
    private String etc;
}

class Book extends Item {
    private String author;
    private String isbn;
}

class Movie extends Item {
    private String director;
    private String actor;
}

데이터베이스 슈퍼타입-서브타입 모델링

슈퍼타입: ITEM 테이블
├── 서브타입: ALBUM 테이블
├── 서브타입: BOOK 테이블
└── 서브타입: MOVIE 테이블

1.2. 상속 관계 매핑 전략

JPA는 객체의 상속 구조를 데이터베이스에 구현하는 세 가지 전략을 제공한다:

전략 설명  주요 어노테이션
조인 전략 각각 테이블로 변환 @Inheritance(strategy = InheritanceType.JOINED)
단일 테이블 전략 통합 테이블로 변환 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
구현 클래스마다 테이블 전략 서브타입 테이블로 변환 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

공통 어노테이션

  • @DiscriminatorColumn: 엔티티 구분 컬럼 지정
  • @DiscriminatorValue: 구분 컬럼에 저장할 값 지정

2. 조인 전략 (JOINED Strategy)

2.1. 개념과 구조

가장 정규화된 형태로, 부모 테이블과 자식 테이블을 각각 생성하고 조인을 통해 데이터를 조회한다.

// 부모 엔티티
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")  // 구분 컬럼
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

// 자식 엔티티 - Album
@Entity
@DiscriminatorValue("ALBUM")  // DTYPE 컬럼 값
public class Album extends Item {
    private String artist;
    private String etc;
}

// 자식 엔티티 - Book
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item {
    private String author;
    private String isbn;
}

// 자식 엔티티 - Movie
@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
    private String director;
    private String actor;
}

2.2. 테이블 구조

생성되는 테이블

-- 부모 테이블
CREATE TABLE ITEM (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    PRICE INTEGER,
    DTYPE VARCHAR(255)  -- 구분 컬럼
);

-- 자식 테이블들
CREATE TABLE ALBUM (
    ID BIGINT PRIMARY KEY,
    ARTIST VARCHAR(255),
    ETC VARCHAR(255),
    FOREIGN KEY (ID) REFERENCES ITEM(ID)
);

CREATE TABLE BOOK (
    ID BIGINT PRIMARY KEY,
    AUTHOR VARCHAR(255),
    ISBN VARCHAR(255),
    FOREIGN KEY (ID) REFERENCES ITEM(ID)
);

CREATE TABLE MOVIE (
    ID BIGINT PRIMARY KEY,
    DIRECTOR VARCHAR(255),
    ACTOR VARCHAR(255),
    FOREIGN KEY (ID) REFERENCES ITEM(ID)
);

2.3. 데이터 저장과 조회

저장 예시

// Album 저장
Album album = new Album();
album.setName("최고의 앨범");
album.setPrice(15000);
album.setArtist("아이유");
album.setEtc("특별 한정판");

em.persist(album);

// 실행되는 SQL
// 1. INSERT INTO ITEM (ID, NAME, PRICE, DTYPE) VALUES (?, ?, ?, 'ALBUM')
// 2. INSERT INTO ALBUM (ID, ARTIST, ETC) VALUES (?, ?, ?)

조회 예시

// 조인을 통한 조회
Album findAlbum = em.find(Album.class, albumId);
// 실행되는 SQL
// SELECT i.*, a.*
// FROM ITEM i
// INNER JOIN ALBUM a ON i.ID = a.ID
// WHERE i.ID = ? AND i.DTYPE = 'ALBUM'

// 부모 타입으로 조회 (다형성 쿼리)
Item item = em.find(Item.class, itemId);
if (item instanceof Album) {
    Album album = (Album) item;
    System.out.println("아티스트: " + album.getArtist());
}

2.4. 장단점 분석

장점

  1. 테이블 정규화: 데이터 중복 최소화
  2. 외래키 참조 무결성: ITEM 테이블의 ID를 참조 가능
    @Entity
    public class OrderItem {
        @ManyToOne
        @JoinColumn(name = "ITEM_ID")  // ITEM 테이블 참조
        private Item item;
    }
    
  3. 저장 공간 효율성: 중복 데이터 없음

단점

  1. 성능 저하: 조회 시 조인이 필요
  2. 복잡한 쿼리: 조인으로 인한 쿼리 복잡도 증가
  3. INSERT 2번: 저장 시 부모/자식 테이블 각각 INSERT

3. 단일 테이블 전략 (SINGLE_TABLE Strategy)

3.1. 개념과 구조

모든 자식 엔티티를 하나의 테이블에 통합하여 저장하는 전략이다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("ALBUM")
public class Album extends Item {
    private String artist;
    private String etc;
}

@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item {
    private String author;
    private String isbn;
}

@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
    private String director;
    private String actor;
}

3.2. 테이블 구조

생성되는 단일 테이블

CREATE TABLE ITEM (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    PRICE INTEGER,
    ARTIST VARCHAR(255),    -- Album 전용
    ETC VARCHAR(255),       -- Album 전용
    AUTHOR VARCHAR(255),    -- Book 전용
    ISBN VARCHAR(255),      -- Book 전용
    DIRECTOR VARCHAR(255),  -- Movie 전용
    ACTOR VARCHAR(255),     -- Movie 전용
    DTYPE VARCHAR(255) NOT NULL  -- 구분 컬럼 (NOT NULL)
);

3.3. 데이터 저장과 조회

저장 예시

Album album = new Album();
album.setName("최고의 앨범");
album.setPrice(15000);
album.setArtist("아이유");
album.setEtc("특별 한정판");

em.persist(album);

// 실행되는 SQL (한 번만 실행)
// INSERT INTO ITEM (ID, NAME, PRICE, ARTIST, ETC, DTYPE)
// VALUES (?, ?, ?, ?, ?, 'ALBUM')

조회 예시

// 단일 테이블 조회 (조인 없음)
Album findAlbum = em.find(Album.class, albumId);
// 실행되는 SQL
// SELECT * FROM ITEM WHERE ID = ? AND DTYPE = 'ALBUM'

// 전체 상품 조회
List<Item> items = em.createQuery(
    "SELECT i FROM Item i", Item.class)
    .getResultList();
// 실행되는 SQL
// SELECT * FROM ITEM

3.4. 장단점 분석

장점

  1. 빠른 조회 성능: 조인 불필요
  2. 단순한 쿼리: 단일 테이블 조회
  3. INSERT 1번: 저장 시 한 번의 INSERT

단점

  1. NULL 허용: 자식 전용 컬럼들은 NULL 허용해야 함
  2. 테이블 비대화: 모든 컬럼이 한 테이블에 모임
  3. 컬럼 수 제한: 데이터베이스별 컬럼 수 제한 존재

4. 구현 클래스마다 테이블 전략 (TABLE_PER_CLASS Strategy)

4.1. 개념과 구조 (비권장)

각각의 자식 엔티티마다 테이블을 생성하는 전략으로, JPA 표준 스펙에 포함되어 있지만 실제 사용은 권장되지 않는다.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

@Entity
public class Album extends Item {
    private String artist;
    private String etc;
}

@Entity
public class Book extends Item {
    private String author;
    private String isbn;
}

@Entity
public class Movie extends Item {
    private String director;
    private String actor;
}

4.2. 테이블 구조

생성되는 테이블들

-- 부모 테이블 없음!
-- 각 자식 테이블에 모든 컬럼 포함

CREATE TABLE ALBUM (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    PRICE INTEGER,
    ARTIST VARCHAR(255),
    ETC VARCHAR(255)
);

CREATE TABLE BOOK (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    PRICE INTEGER,
    AUTHOR VARCHAR(255),
    ISBN VARCHAR(255)
);

CREATE TABLE MOVIE (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    PRICE INTEGER,
    DIRECTOR VARCHAR(255),
    ACTOR VARCHAR(255)
);

4.3. 데이터 저장과 조회

저장 예시

Album album = new Album();
album.setName("최고의 앨범");
album.setPrice(15000);
album.setArtist("아이유");

em.persist(album);
// INSERT INTO ALBUM (ID, NAME, PRICE, ARTIST, ETC) VALUES (?, ?, ?, ?, ?)

조회 예시 (문제점)

// 부모 타입으로 조회 시 UNION 사용
Item item = em.find(Item.class, itemId);
// 실행되는 SQL
// SELECT * FROM (
//   SELECT ID, NAME, PRICE, ARTIST, ETC, 'ALBUM' as DTYPE FROM ALBUM
//   UNION ALL
//   SELECT ID, NAME, PRICE, AUTHOR, ISBN, 'BOOK' as DTYPE FROM BOOK
//   UNION ALL
//   SELECT ID, NAME, PRICE, DIRECTOR, ACTOR, 'MOVIE' as DTYPE FROM MOVIE
// ) WHERE ID = ?

// JPQL 사용 시
List<Item> items = em.createQuery(
    "SELECT i FROM Item i", Item.class)
    .getResultList();
// 모든 테이블을 UNION으로 조회 (성능 저하)

4.4. 장단점 분석

장점

  1. 서브타입 구분 명확: 테이블이 분리되어 있음
  2. NOT NULL 제약조건 사용 가능: 각 테이블별 제약조건 설정 가능

단점 (심각한 문제점)

  1. UNION 쿼리: 여러 테이블 조회 시 UNION 사용 (성능 저하)
  2. 통합 쿼리 어려움: 모든 자식 테이블 조회해야 함
  3. 외래키 참조 문제: ITEM 테이블이 없어 참조 불가능
  4. 확장성 문제: 새로운 자식 타입 추가 시 기존 쿼리 모두 영향

5. @MappedSuperclass

5.1. 개념과 목적

상속 관계 매핑이 아닌, 공통 매핑 정보를 상속하기 위한 기능이다. 엔티티가 아니므로 테이블과 매핑되지 않는다.

// 공통 속성을 모은 추상 클래스
@MappedSuperclass
public abstract class BaseEntity {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @Column(updatable = false)
    private LocalDateTime createdDate;

    private LocalDateTime lastModifiedDate;

    // 생성자, getter/setter
}

// BaseEntity 상속
@Entity
public class Member extends BaseEntity {
    private String email;
    private int age;
}

@Entity
public class Product extends BaseEntity {
    private int price;
    private int stockQuantity;
}

5.2. 테이블 구조

생성되는 테이블

-- 각 엔티티에 BaseEntity의 필드들이 포함됨

CREATE TABLE MEMBER (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    CREATED_DATE TIMESTAMP,
    LAST_MODIFIED_DATE TIMESTAMP,
    EMAIL VARCHAR(255),
    AGE INTEGER
);

CREATE TABLE PRODUCT (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    CREATED_DATE TIMESTAMP,
    LAST_MODIFIED_DATE TIMESTAMP,
    PRICE INTEGER,
    STOCK_QUANTITY INTEGER
);

5.3. 특징과 제약사항

주요 특징

  1. 엔티티가 아님: em.find(BaseEntity.class, id) 불가능
  2. 테이블과 매핑 안됨: BaseEntity 테이블 생성되지 않음
  3. 매핑 정보 제공만: 상속받는 엔티티에 매핑 정보 전달
  4. 추상 클래스 권장: 직접 생성/사용할 일이 없음

사용 예시

@MappedSuperclass
public abstract class BaseTimeEntity {

    @Column(updatable = false)
    private LocalDateTime createdDate;

    @PrePersist
    public void prePersist() {
        this.createdDate = LocalDateTime.now();
    }

    @Column
    private LocalDateTime updatedDate;

    @PreUpdate
    public void preUpdate() {
        this.updatedDate = LocalDateTime.now();
    }
}

// 감사 정보 추가
@MappedSuperclass
public abstract class BaseEntity extends BaseTimeEntity {

    @CreatedBy
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
}

5.4. @MappedSuperclass vs @Inheritance

비교 항목 @MappedSuperclass @Inheritance
목적 공통 매핑 정보 상속 객체 상속 구조 매핑
테이블 테이블 생성 안됨 테이블 생성됨
엔티티 엔티티 아님 엔티티임
조회 직접 조회 불가 다형성 조회 가능
참조 참조 관계 불가 참조 관계 가능

6. 실전 예제: 상속관계 매핑 적용

6.1. 요구사항 추가

기존 주문 시스템에 다음과 같은 요구사항이 추가되었다:

  1. 상품 종류: 음반(Album), 도서(Book), 영화(Movie) - 이후 확장 가능
  2. 모든 엔티티에 등록일과 수정일 필수

6.2. 도메인 모델 설계

// 공통 시간 정보 (MappedSuperclass)
@MappedSuperclass
@Getter @Setter
public abstract class BaseTimeEntity {

    @Column(name = "CREATED_DATE", updatable = false)
    private LocalDateTime createdDate;

    @Column(name = "MODIFIED_DATE")
    private LocalDateTime modifiedDate;

    @PrePersist
    public void prePersist() {
        this.createdDate = LocalDateTime.now();
        this.modifiedDate = this.createdDate;
    }

    @PreUpdate
    public void preUpdate() {
        this.modifiedDate = LocalDateTime.now();
    }
}

// 상품 상속 구조 (조인 전략 사용)
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "PRODUCT_TYPE")
@Table(name = "PRODUCT")
public abstract class Product extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private int price;

    @Column(name = "STOCK_QUANTITY")
    private int stockQuantity;

    // 공통 비즈니스 메서드
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }

    public void removeStock(int quantity) {
        int restStock = this.stockQuantity - quantity;
        if (restStock < 0) {
            throw new NotEnoughStockException("재고가 부족합니다.");
        }
        this.stockQuantity = restStock;
    }
}

// 앨범 상품
@Entity
@DiscriminatorValue("ALBUM")
@Table(name = "ALBUM")
public class Album extends Product {

    @Column(name = "ARTIST")
    private String artist;

    @Column(name = "ETC")
    private String etc;

    @Column(name = "GENRE")
    private String genre;

    @Column(name = "RELEASE_DATE")
    private LocalDate releaseDate;
}

// 도서 상품
@Entity
@DiscriminatorValue("BOOK")
@Table(name = "BOOK")
public class Book extends Product {

    @Column(name = "AUTHOR")
    private String author;

    @Column(name = "ISBN", unique = true)
    private String isbn;

    @Column(name = "PUBLISHER")
    private String publisher;

    @Column(name = "PAGE_COUNT")
    private int pageCount;
}

// 영화 상품
@Entity
@DiscriminatorValue("MOVIE")
@Table(name = "MOVIE")
public class Movie extends Product {

    @Column(name = "DIRECTOR")
    private String director;

    @Column(name = "ACTOR")
    private String actor;

    @Column(name = "DURATION_MINUTES")
    private int durationMinutes;

    @Column(name = "RATING")
    private String rating;  // 12세, 15세, 19세 등
}

// 회원에도 BaseTimeEntity 적용
@Entity
@Table(name = "MEMBER")
public class Member extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    @Embedded
    private Address address;
}

// 주문에도 BaseTimeEntity 적용
@Entity
@Table(name = "ORDERS")
public class Order extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "DELIVERY_ID")
    private Delivery delivery;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}

6.3. 테이블 설계

상품 관련 테이블

-- 공통 상품 테이블
CREATE TABLE PRODUCT (
    ID BIGINT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(255) NOT NULL,
    PRICE INTEGER NOT NULL,
    STOCK_QUANTITY INTEGER,
    CREATED_DATE TIMESTAMP,
    MODIFIED_DATE TIMESTAMP,
    PRODUCT_TYPE VARCHAR(255) NOT NULL
);

-- 앨범 테이블
CREATE TABLE ALBUM (
    ID BIGINT PRIMARY KEY,
    ARTIST VARCHAR(255),
    ETC VARCHAR(255),
    GENRE VARCHAR(255),
    RELEASE_DATE DATE,
    FOREIGN KEY (ID) REFERENCES PRODUCT(ID)
);

-- 도서 테이블
CREATE TABLE BOOK (
    ID BIGINT PRIMARY KEY,
    AUTHOR VARCHAR(255),
    ISBN VARCHAR(255) UNIQUE,
    PUBLISHER VARCHAR(255),
    PAGE_COUNT INTEGER,
    FOREIGN KEY (ID) REFERENCES PRODUCT(ID)
);

-- 영화 테이블
CREATE TABLE MOVIE (
    ID BIGINT PRIMARY KEY,
    DIRECTOR VARCHAR(255),
    ACTOR VARCHAR(255),
    DURATION_MINUTES INTEGER,
    RATING VARCHAR(255),
    FOREIGN KEY (ID) REFERENCES PRODUCT(ID)
);

공통 시간 컬럼이 추가된 테이블

-- 회원 테이블
CREATE TABLE MEMBER (
    ID BIGINT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(255) NOT NULL,
    EMAIL VARCHAR(255) NOT NULL UNIQUE,
    CITY VARCHAR(255),
    STREET VARCHAR(255),
    ZIPCODE VARCHAR(255),
    CREATED_DATE TIMESTAMP,
    MODIFIED_DATE TIMESTAMP
);

-- 주문 테이블
CREATE TABLE ORDERS (
    ID BIGINT PRIMARY KEY AUTO_INCREMENT,
    MEMBER_ID BIGINT,
    DELIVERY_ID BIGINT,
    STATUS VARCHAR(255),
    CREATED_DATE TIMESTAMP,
    MODIFIED_DATE TIMESTAMP,
    FOREIGN KEY (MEMBER_ID) REFERENCES MEMBER(ID),
    FOREIGN KEY (DELIVERY_ID) REFERENCES DELIVERY(ID)
);

6.4. 비즈니스 로직 예시

@Service
@Transactional
public class ProductService {

    @PersistenceContext
    private EntityManager em;

    // 앨범 등록
    public Long registerAlbum(AlbumDto albumDto) {
        Album album = new Album();
        album.setName(albumDto.getName());
        album.setPrice(albumDto.getPrice());
        album.setArtist(albumDto.getArtist());
        album.setGenre(albumDto.getGenre());
        album.setReleaseDate(albumDto.getReleaseDate());
        album.setStockQuantity(albumDto.getStockQuantity());

        em.persist(album);
        return album.getId();
    }

    // 상품 조회 (다형성 활용)
    public ProductDto getProduct(Long productId) {
        Product product = em.find(Product.class, productId);

        if (product instanceof Album) {
            Album album = (Album) product;
            return new AlbumDto(album);
        } else if (product instanceof Book) {
            Book book = (Book) product;
            return new BookDto(book);
        } else if (product instanceof Movie) {
            Movie movie = (Movie) product;
            return new MovieDto(movie);
        }

        throw new ProductNotFoundException("상품을 찾을 수 없습니다.");
    }

    // 특정 타입의 상품들 조회
    public List<Album> getAlbumsByArtist(String artist) {
        return em.createQuery(
            "SELECT a FROM Album a WHERE a.artist = :artist",
            Album.class)
            .setParameter("artist", artist)
            .getResultList();
    }

    // 모든 상품 조회 (다형성 쿼리)
    public List<Product> getAllProducts() {
        return em.createQuery(
            "SELECT p FROM Product p ORDER BY p.createdDate DESC",
            Product.class)
            .getResultList();
    }
}

7. 전략 선택 가이드라인

7.1. 상속 전략 선택 기준

조인 전략 (JOINED) 사용 시

  • 객체의 상속 구조를 정확히 표현해야 할 때
  • 정규화된 데이터 구조가 필요할 때
  • 다양한 타입의 확장이 예상될 때
  • 외래키 참조 무결성이 중요할 때

단일 테이블 전략 (SINGLE_TABLE) 사용 시

  • 조회 성능이 가장 중요할 때
  • 자식 엔티티의 컬럼이 적을 때
  • NULL 허용이 문제되지 않을 때
  • 단순한 도메인 구조일 때

구현 클래스마다 테이블 전략 (TABLE_PER_CLASS)

  • 사용하지 말 것

7.2. @MappedSuperclass 사용 시기

다음과 같은 경우에 @MappedSuperclass를 사용한다:

  1. 공통 속성이 많을 때
    // 등록일, 수정일, 등록자, 수정자 등
    @MappedSuperclass
    public abstract class AuditEntity {
        private LocalDateTime createdDate;
        private String createdBy;
        private LocalDateTime modifiedDate;
        private String modifiedBy;
    }
    
  2. 엔티티가 아닌 공통 기능이 필요할 때
    // 비즈니스 키를 가진 엔티티
    @MappedSuperclass
    public abstract class BusinessKeyEntity {
        @Column(unique = true, nullable = false)
        private String businessKey;
    
        public abstract String generateBusinessKey();
    }
    
  3. 테이블 생성 없이 속성만 상속받고 싶을 때

7.3. 실무 권장사항

  1. 상속 관계가 단순할 때: 단일 테이블 전략 고려
  2. 상속 관계가 복잡하거나 확장 가능성 있을 때: 조인 전략 사용
  3. 공통 속성은 항상: @MappedSuperclass로 분리
  4. 기본값 설정: 조인 전략을 기본으로 고려
    @Inheritance(strategy = InheritanceType.JOINED)  // 기본 전략
    @DiscriminatorColumn(name = "DTYPE")
    

8. 정리

고급 매핑 기능은 JPA의 강력한 기능이지만, 신중하게 사용해야 한다:

  1. 상속 매핑은 신중하게
    • 객체 상속과 테이블 설계의 괴리를 이해할 것
    • 프로젝트 특성에 맞는 전략 선택할 것
    • 성능과 유지보수성 균형 고려할 것
  2. @MappedSuperclass는 적극 활용
    • 공통 속성과 행위 추출할 것
    • 코드 중복 제거와 일관성 유지할 것
    • Audit 정보 같은 공통 관심사 분리할 것
  3. 확장성 고려
    • 새로운 상품 타입 추가를 염두에 둘 것
    • 다형성 쿼리와 타입 캐스팅 활용할 것
    • DTO 변환 로직 효율적으로 설계할 것

상속 관계 매핑은 객체지향의 강력한 특성을 데이터베이스에 잘 반영할 수 있게 해주지만, 각 전략의 장단점을 충분히 이해하고 프로젝트의 요구사항에 맞게 선택하는 것이 중요하다. @MappedSuperclass는 코드 재사용과 유지보수성을 높이는 간단하면서도 효과적인 방법이다.

'Spring > JPA' 카테고리의 다른 글

[Basic-8] 값 타입  (0) 2026.01.06
[Basic-7] 프록시와 연관관계 관리  (0) 2026.01.06
[Basic-5] 연관관계 매핑 심화  (0) 2026.01.06
[Basic-4] 연관관계 매핑 기초  (0) 2026.01.06
[Basic-3] 엔터티 매핑 (Entity Mapping)  (0) 2026.01.06
'Spring/JPA' 카테고리의 다른 글
  • [Basic-8] 값 타입
  • [Basic-7] 프록시와 연관관계 관리
  • [Basic-5] 연관관계 매핑 심화
  • [Basic-4] 연관관계 매핑 기초
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (250) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (25) N
        • MSA 기본 (11)
        • MSA 아키텍처 (14) N
      • Kafka (30)
        • Core (18)
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[Basic-6] 고급 관계 매핑
상단으로

티스토리툴바