일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 스프링
- Spring
- 코딩테스트
- Baekjoon
- API
- database
- spring boot
- 클린코드
- 데이터베이스
- 자바
- 개발자
- 알고리즘
- framework
- ES
- 읽기쉬운코드
- 그리디
- 프레임워크
- 코드
- Java
- 코딩
- 개발
- Elasticsearch
- JPA
- cleancode
- 백준
- mongoDB
- 엘라스틱서치
- 애자일기법
- 애자일프로그래밍
- 그리디알고리즘
- Today
- Total
튼튼발자 개발 성장기🏋️
Deep한 JPA 매핑 본문
1. 상속 관계 매핑
객체 지향 프로그래밍에서 상속은 중요한 개념인 것은 누구나 알고 있는 사실이다. JPA에서는 이러한 상속 관계를 데이터베이스 테이블에 매핑하는 여러 가지 전략을 제공한다.
1.1. 조인 전략 (Joined Strategy)
조인 전략은 부모 클래스와 자식 클래스 각각에 대한 테이블을 생성하고, 자식 클래스의 테이블은 부모 클래스의 기본 키를 외래 키로 참조하는 방식이다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Book extends Item {
private String author;
private String isbn;
}
@Entity
public class Album extends Item {
private String artist;
private String genre;
}
- 장점:
- 정규화된 구조로, 데이터 중복이 발생하지 않습니다.
- 여러 자식 엔티티에서 공통된 부모 엔티티의 필드들을 재사용할 수 있습니다. (효율적인 저장 공간)
- 단점:
- 조인이 자주 발생하므로, 복잡한 쿼리에서 성능 이슈가 발생할 수 있습니다.
- 쿼리가 복잡해질 수 있으며, 많은 테이블을 조인해야 하는 상황에서는 성능이 저하될 수 있습니다.
- 데이터를 저장하는 insert query가 두 번 실행된다.
1.2. 단일 테이블 전략 (Single Table Strategy)
단일 테이블 전략은 부모 클래스와 자식 클래스 모두를 하나의 테이블에 저장하는 방식이다. 단, 자식 클래스마다 구분되는 필드가 있을 경우, 그 필드들은 NULL이 될 수 있다. 조회 시 조인을 사용하지 않아서 일반적으로는 가장 빠를 수 있지만, 테이블의 크기가 클 수록 성능이 저하될 수 있다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
private String isbn;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
private String genre;
}
- 장점:
- 쿼리가 간단해지고, 성능이 조인 전략에 비해 상대적으로 우수하다.
- 테이블 간의 조인이 발생하지 않아 단순한 조회 쿼리의 성능이 좋다.
- 단점:
- 테이블에 불필요한 NULL 값이 많이 생길 수 있다.
- 자식 클래스가 많아질수록 테이블이 비대해져 관리가 어려워질 수 있다.
1.3. 구현 클래스마다 테이블 전략 (Table per Class Strategy)
Table per Class Strategy 전략은 부모 클래스의 정보를 자식 클래스가 각각의 테이블로 독립적으로 구현하는 방식이다. 여러 자식 테이블을 함께 조회하면 UNION 절을 사용해야하기 때문에 성능이 느리다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Book extends Item {
private String author;
private String isbn;
}
@Entity
public class Album extends Item {
private String artist;
private String genre;
}
- 장점:
- 서브타입을 구분해서 처리할 때 효과적이다.
- not null 제약 조건을 사용할 수 있다.
- 단점:
- 부모 클래스의 필드가 중복 저장됩니다.
- 자식 테이블을 통합해서 query하기 쉽지 않다.
- 데이터를 조회할 때 UNION 연산이 필요할 수 있어 성능이 저하될 수 있다.
2. @MappedSuperclass Annotation
@MappedSuperclass는 JPA에서 상속받을 수 있는 엔티티 클래스의 공통 매핑 정보를 정의할 때 사용된다. 데이터베이스에 매핑되지 않지만, 이 클래스에서 정의된 필드들은 자식 엔티티에 상속된다. 즉, 테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용한다. 그 말은 즉슨 @MappedSuperclass로 지정한 클래스는 엔티티가 아니기 때문에 JPQL에서 사용할 수 없다는 이야기다.
부모로부터 물려받은 매핑 정보를 재정의할 수도 있다.
- @AttributeOverrides: 여러 필드의 매핑을 재정의할 때 사용한다. 내부적으로 여러 @AttributeOverride를 포함할 수 있다.
- @AttributeOverride: 단일 필드의 매핑을 재정의할 때 사용한다.
- name: 재정의할 부모 클래스의 필드 이름을 지정한다.
- column: 재정의할 필드의 새로운 매핑 정보를 지정한다.
@MappedSuperclass
public abstract class BaseEntity {
@Id @GeneratedValue
private Long id;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
@Entity
@AttributeOverrides({
@AttributeOverride(name = "createdAt", column = @Column(name = "user_created_at")),
@AttributeOverride(name = "updatedAt", column = @Column(name = "user_updated_at"))
})
public class User extends BaseEntity {
private String username;
private String password;
}
@Entity
public class Product extends BaseEntity {
private String name;
private Double price;
}
3. 복합 키와 식별 관계 매핑
복합 키는 두 개 이상의 컬럼을 묶어 하나의 기본 키로 사용한다. 식별 관계는 부모 엔티티의 기본 키가 자식 엔티티의 기본 키에도 포함되는 관계를 의미한다.
3.1 식별 관계 vs 비식별 관계
- 식별 관계: 자식 엔티티가 부모 엔티티의 기본 키를 함께 포함하는 관계.
- 비식별 관계: 자식 엔티티가 독립적인 기본 키를 가지며, 부모 엔티티의 기본 키는 외래 키로만 사용되는 관계. (보편적으로 많이 사용되는 관계)
- 필수적 비식별 관계: 자식 엔티티가 항상 부모 엔티티와 연관되어 있어야 하는 경우를 말한다. 즉, 자식 엔티티가 반드시 부모 엔티티의 존재를 필요로 하며, 부모 엔티티가 없으면 자식 엔티티가 존재할 수 없다.
- 선택 적 비식별 관계: 자식 엔티티가 부모 엔티티와 연관될 수도, 연관되지 않을 수도 있는 경우를 말한다. 즉, 자식 엔티티가 독립적으로 존재할 수 있으며, 부모 엔티티와의 관계가 선택적이다.
3.2 복합 키: 비식별 관계 매핑
JPA는 복합키를 지원하기 위해 @IdClass와 @EmbeddedId 2가지 어노테이션을 제공해주는데 @IdClass는 RDB에 가까운 방법이고 @EmbeddedId는 객체지향에 가까운 방법이다. 나는 @EmbeddedId를 선호하기 때문에 @EmbeddedId만 다룬다.
@EmbeddedId를 사용한다면 아래와 같이 조건을 만족해야한다.
- public class
- Serializable를 상속
- @Embeddable 어노테이션을 지정
- equals()와 hashCode() 메서드를 구현
영속성 컨텍스트는 엔티티의 식별자를 키로 사용해서 엔티티를 관리한다는 점은 지난 시간에 알아보았다. 여기서 엔티티를 비교할 때 사용하는 것이 equals()와 hashCode() 메서드다. 즉 식별자 객체의 동등성이 지켜지지 않으면 예상과 다른 엔티티가 조회될 수도 있고 찾을 수도 없게 되기도 하는 등의 심각한 문제가 생길 수 있다.
@Entity
public class Parent {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Child {
@EmbeddedId
private ChildId id;
@MapsId("parentId")
@ManyToOne
private Parent parent;
private String name;
}
@Embeddable
@EqualsAndHashCode
public class ChildId implements Serializable {
private Long parentId;
private String uniqueId;
}
3.3 복합 키: 식별 관계 매핑
@Entity
public class Parent {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
public class Child {
@EmbeddedId
private ChildId id;
@MapsId("parentId")
@ManyToOne
private Parent parent;
private String name;
}
@Embeddable
@EqualsAndHashCode
public class ChildId implements Serializable {
private Long parentId;
private String childId;
}
3.4 비식별 관계로 구현
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
private String orderNumber;
}
@Entity
public class OrderItem {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
private String productName;
}
3.5 일대일 식별 관계
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String username;
}
@Entity
public class UserProfile {
@Id
private Long id;
@OneToOne
@MapsId
@JoinColumn(name = "id")
private User user;
private String address;
}
3.6 식별, 비식별 관계의 장단점
- 식별 관계의 장점: 부모와 자식의 관계가 명확하며, 일관된 식별자를 사용하여 데이터 무결성을 높일 수 있다.
- 식별 관계의 단점: 기본 키가 복잡해질 수 있으며, 복합 키로 인해 쿼리가 복잡해질 수 있다.
- 비식별 관계의 장점: 단순한 기본 키를 사용할 수 있어 관리가 쉽다.
- 비식별 관계의 단점: 데이터 무결성을 보장하기 위해 추가적인 제약 조건이 필요할 수 있다.
4. 조인 테이블
조인 테이블은 엔티티 간의 관계를 표현하기 위해 중간 테이블을 사용하는 방식이다.
4.1 일대일 조인 테이블
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String username;
@OneToOne
@JoinTable(name = "USER_PROFILE",
joinColumns = @JoinColumn(name = "USER_ID"),
inverseJoinColumns = @JoinColumn(name = "PROFILE_ID"))
private UserProfile profile;
}
@Entity
public class UserProfile {
@Id @GeneratedValue
private Long id;
private String address;
}
4.2 일대다 조인 테이블
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String username;
@OneToMany
@JoinTable(name = "USER_ORDERS",
joinColumns = @JoinColumn(name = "USER_ID"),
inverseJoinColumns = @JoinColumn(name = "ORDER_ID"))
private List<Order> orders;
}
4.3 다대일 조인 테이블
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
private String orderNumber;
@ManyToOne
@JoinTable(name = "USER_ORDERS",
joinColumns = @JoinColumn(name = "ORDER_ID"),
inverseJoinColumns = @JoinColumn(name = "USER_ID"))
private User user;
}
4.4 다대다 조인 테이블
@Entity
public class Student {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "STUDENT_COURSE",
joinColumns = @JoinColumn(name = "STUDENT_ID"),
inverseJoinColumns = @JoinColumn(name = "COURSE_ID"))
private List<Course> courses;
}
@Entity
public class Course {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy = "courses")
private List<Student> students;
}
5. 엔티티 하나에 여러 테이블 매핑
잘 사용되는 매핑은 아니지만, 하나의 엔티티에 여러 테이블을 매핑하는 방법이 가능하다. 이 매핑 방법은 항상 두 테이블을 조회하므로 최적화하기 어렵기 때문에 테이블당 엔티티를 각각 만들어서 일대일 매핑하는 것을 권장한다.
@Entity
@Table(name = "USER")
@SecondaryTable(name = "USER_DETAILS", pkJoinColumns = @PrimaryKeyJoinColumn(name = "USER_ID"))
public class User {
@Id @GeneratedValue
private Long id;
private String username;
@Column(table = "USER_DETAILS")
private String address;
@Column(table = "USER_DETAILS")
private String phoneNumber;
}
'API > JPA' 카테고리의 다른 글
값 타입 (0) | 2024.09.02 |
---|---|
프록시와 연관관계 관리 (0) | 2024.08.27 |
연관관계 매핑 기초 (0) | 2024.08.18 |
영속성 관리 (0) | 2024.08.17 |
Entity 매핑 #1 (0) | 2021.01.13 |