이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

⭐ 영속성 전이: CASCADE

특정 Entity를 영속 상태로 만들 때, 연관된 Entity도 함께 영속상태로 만들고 싶을 때 사용

 

⌨️ 코드

Entity 객체

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
31
32
33
34
35
36
37
38
39
40
41
@Entity
public class Parent {
 
    @Id @GeneratedValue
    private long id;
 
    private String name;
 
    @OneToMany(mappedBy = "parent")
    private List<Child> children = new ArrayList<>();
 
    public void addChild(Child child){
        children.add(child);
        child.setParent(this);
    }
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<Child> getChildren() {
        return children;
    }
 
    public void setChildren(List<Child> children) {
        this.children = children;
    }
}
 
 

main method

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
31
32
33
34
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Child child1 = new Child();
            Child child2 = new Child();
 
            Parent parent = new Parent();
            parent.addChild(child1);
            parent.addChild(child2);
 
            em.persist(parent);
 
            em.flush();
            em.clear();
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
 
        emf.close();
 
    }
 
}
 
 

 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
Hibernate: 
    /* insert jpa_basic.Parent
        */ insert 
        into
            Parent
            (name, id) 
        values
            (?, ?)
 
 

Child 객체는 저장되지 않고, Parent 객체만 저장됨 

 

⭐CascadeType.All 적용 후,

Entity 객체

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
31
32
33
34
35
36
37
38
39
40
@Entity
public class Parent {
 
    @Id @GeneratedValue
    private long id;
 
    private String name;
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Child> children = new ArrayList<>();
 
    public void addChild(Child child){
        children.add(child);
        child.setParent(this);
    }
 
    public long getId() {
        return id;
    }
 
    public void setId(long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<Child> getChildren() {
        return children;
    }
 
    public void setChildren(List<Child> children) {
        this.children = children;
    }
}
 
 

 

🖨️발생한 쿼리

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
Hibernate: 
    /* insert jpa_basic.Parent
        */ insert 
        into
            Parent
            (name, id) 
        values
            (?, ?)
 
Hibernate: 
    /* insert jpa_basic.Child
        */ insert 
        into
            Child
            (name, parent_id, id) 
        values
            (?, ?, ?)
 
Hibernate: 
    /* insert jpa_basic.Child
        */ insert 
        into
            Child
            (name, parent_id, id) 
        values
            (?, ?, ?)
 
 

Parent 객체와 함께 Child 객체도 저장

 

- CASCADE 종류(일부)

    - ALL: 모두 적용

    - PERSIST: 영속

 

🚨 부모 객체로 인한 변경 시, 다른 객체에 영향을 미칠 수 있으므로 CASCADE 사용 시, 참조하는 곳이 하나일 때만 사용해야함

 

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

[JPA_Basic] 값 타입 컬렉션  (0) 2023.09.19
[JPA_Basic] 값 타입과 불변 객체  (0) 2023.09.14
[JPA_Basic] N+1 문제  (0) 2023.09.10
[JPA_Basic] Proxy  (0) 2023.09.08
[JPA_Basic] @MappedSuperclass  (0) 2023.09.05

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

⭐ N + 1 문제

: 연관관계가 설정된 엔티티 사이에서 하나의 엔티티를 조회했을 때, 조회된 엔티티의 개수(N 개)만큼 연관된 엔티티를 조회하기 위해 추가적인 쿼리가 발생하는 문제

    - 1: 하나의 엔티티를 조회하기 위한 쿼리의 개수

    - N: 연관된 데이터를 조회하기 위한 추가적인 쿼리의 개수

 

 

⌨️ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class Member extends BaseEntity {
    
    // 생략
 
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
 
    @Column(name = "USERNAME")
    private String name;
 
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
 
    // 생략
 
}
 
 
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Main {
    public static void main(String[] args) {
 
        // 생략
 
        try {
 
            Team teamA = new Team();
            teamA.setName("teamA");
            em.persist(teamA);
 
            Team teamB = new Team();
            teamB.setName("teamB");
            em.persist(teamB);
 
            Member member1 = new Member();
            member1.setName("user1");
            member1.setTeam(teamA);
            em.persist(member1);
 
            Member member2 = new Member();
            member2.setName("user2");
            member2.setTeam(teamB);
            em.persist(member2);
 
            em.flush();
            em.clear();
 
            List<Member> members = em.createQuery("select m from Member m", Member.class)
                    .getResultList();
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }
 
        emf.close();
 
    }
 
}
 
 

 

 

🖨️발생한 쿼리

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
31
32
33
34
35
36
37
38
39
40
41
Hibernate: 
    /* select
        m 
    from
        Member m */ select
            member0_.MEMBER_ID as member_i1_3_,
            member0_.INSERT_MEMBER as insert_m2_3_,
            member0_.createDate as createda3_3_,
            member0_.UPDATE_MEMBER as update_m4_3_,
            member0_.lastModifiedDate as lastmodi5_3_,
            member0_.USERNAME as username6_3_,
            member0_.TEAM_ID as team_id7_3_ 
        from
            Member member0_
 
Hibernate: 
    select
        team0_.TEAM_ID as team_id1_5_0_,
        team0_.INSERT_MEMBER as insert_m2_5_0_,
        team0_.createDate as createda3_5_0_,
        team0_.UPDATE_MEMBER as update_m4_5_0_,
        team0_.lastModifiedDate as lastmodi5_5_0_,
        team0_.name as name6_5_0_ 
    from
        Team team0_ 
    where
        team0_.TEAM_ID=?
 
Hibernate: 
    select
        team0_.TEAM_ID as team_id1_5_0_,
        team0_.INSERT_MEMBER as insert_m2_5_0_,
        team0_.createDate as createda3_5_0_,
        team0_.UPDATE_MEMBER as update_m4_5_0_,
        team0_.lastModifiedDate as lastmodi5_5_0_,
        team0_.name as name6_5_0_ 
    from
        Team team0_ 
    where
        team0_.TEAM_ID=?
 
 

 

 

📡 원인

1. Member 객체를 조회하기 위한 쿼리 발생

2. FetchType.EAGER의 경우, 필드값이 채워져 있어야하므로,

    - TeamA를 가져오기 위한 Query1

    - TeamB를 가져오기 위한 Query2

 

 

📰 해결 방법

1. FetchType.LAZY 선언

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class Member extends BaseEntity {
    
    // 생략
 
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
 
    @Column(name = "USERNAME")
    private String name;
 
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
 
    // 생략
 
}
 
 

🖨️발생한 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hibernate: 
    /* select
        m 
    from
        Member m */ select
            member0_.MEMBER_ID as member_i1_3_,
            member0_.INSERT_MEMBER as insert_m2_3_,
            member0_.createDate as createda3_3_,
            member0_.UPDATE_MEMBER as update_m4_3_,
            member0_.lastModifiedDate as lastmodi5_3_,
            member0_.USERNAME as username6_3_,
            member0_.TEAM_ID as team_id7_3_ 
        from
            Member member0_
 
 

 

2. fetch join 활용

* fetch join: 연관된 엔티티나 컬렉션을 함께 로딩하여 성능을 최적화하는 기능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class Member extends BaseEntity {
    
    // 생략
 
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
 
    @Column(name = "USERNAME")
    private String name;
 
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
 
    // 생략
 
}
 
 

🖨️발생한 쿼리

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
Hibernate: 
    /* select
        m 
    from
        Member m 
    join
        fetch m.team */ select
            member0_.MEMBER_ID as member_i1_3_0_,
            team1_.TEAM_ID as team_id1_5_1_,
            member0_.INSERT_MEMBER as insert_m2_3_0_,
            member0_.createDate as createda3_3_0_,
            member0_.UPDATE_MEMBER as update_m4_3_0_,
            member0_.lastModifiedDate as lastmodi5_3_0_,
            member0_.USERNAME as username6_3_0_,
            member0_.TEAM_ID as team_id7_3_0_,
            team1_.INSERT_MEMBER as insert_m2_5_1_,
            team1_.createDate as createda3_5_1_,
            team1_.UPDATE_MEMBER as update_m4_5_1_,
            team1_.lastModifiedDate as lastmodi5_5_1_,
            team1_.name as name6_5_1_ 
        from
            Member member0_ 
        inner join
            Team team1_ 
                on member0_.TEAM_ID=team1_.TEAM_ID
 
 

 

 

cf. @ManyToOne, @OneToOne: default - FetchType.EAGER

▶ FetchType.LAZY 필수 기재

 

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

[JPA_Basic] 값 타입과 불변 객체  (0) 2023.09.14
[JPA_Basic] Cascade  (0) 2023.09.11
[JPA_Basic] Proxy  (0) 2023.09.08
[JPA_Basic] @MappedSuperclass  (0) 2023.09.05
[JPA_Basic] 상속관계 매핑  (0) 2023.08.23

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

em.find();와 em.getReference();

em.find(): DB에서 실제 Enitty 객체 조회

em.getReference(): DB 조회를 미루는 Proxy Entity 객체 조회

 

 

Proxy 객체

- 실제 클래스를 상속받아 만들어짐

- 프록시 객체는 실제 객체의 참조를 보관

- 프록시 객체 호출 시, 프록시 객체는 실제 객체의 메서드 호출

* Proxy 객체의 초기화: Proxy 객체의 Target Entity를 생성하는 과정

1. getName() 요청

2. Proxy 객체의 target이 연결되어있는지 확인

→ 없을 경우, 영속성 컨텍스트에 target에 연결할 Entity 객체 요청

3. 영속성 컨텍스트에서 DB 조회 후,

4. 실제 Entity 객체 생성하여 반환

5. 반환된 Entity 객체를 Proxy 객체의 target에 연결

 

 

Proxy 객체 특징

- Proxy 객체는 처음 사용할 때 한 번만 초기화

- Proxy 객체를 초기화하더라도 Entity 객체로 바뀌는 것이 아니라, Proxy 객체를 통해서 실제 Entity 객체에 접근하는 것

    * 타입 체크 시, == 비교가 아닌 instance of 사용

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
31
32
33
34
35
36
37
38
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            Member member2 = new Member();
            member2.setName("user2");
            em.persist(member2);
 
            em.flush();
            em.clear();
 
            Member m1 = em.find(Member.class, member1.getId());
            Member m2 = em.getReference(Member.class, member2.getId());
 
            logic(m1, m2);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
 
    private static void logic(Member m1, Member m2) {
        System.out.println("m1 == m2: " + (m1.getClass() == m2.getClass()));
    }
 
}
 
 

🚨 logic()을 구현하여, getClass == 비교 시 false가 반환 됨

    - m1.getClass(): Entity

    - m2.getClass(): Proxy

m1, m2가 어떤 객체로 넘어올 지 알 수 없으므로 instance of 사용

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
31
32
33
34
35
36
37
38
39
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            Member member2 = new Member();
            member2.setName("user2");
            em.persist(member2);
 
            em.flush();
            em.clear();
 
            Member m1 = em.find(Member.class, member1.getId());
            Member m2 = em.getReference(Member.class, member2.getId());
 
            logic(m1, m2);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
 
    private static void logic(Member m1, Member m2) {
        System.out.println("m1 Class: " + (m1 instanceof Member));
        System.out.println("m2 Class: " + (m2 instanceof Member));
    }
 
}
 
 

 

- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면, em.getReference()를 호출해도 실제 Entity 반환

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
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member findMember = em.find(Member.class, member1.getId());
            System.out.println("findMember.id = " + findMember.getClass());
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

refMember.getClass()가 Entity 객체로 반환되는 이유

    - 같은 영속성 컨텍스트 안에 같은 PK를 통해 가져온 객체는 == 비교 시, JPA는 항상 true를 반환해야 함

    - 영속성 컨텍스트에 존재하는 Entity 대신 Proxy를 가져올 이점이 없음

+ 만일 getReference()로 Proxy 객체를 먼저 조회할 경우는 findMember가 Proxy 객체가 반환됨

    - 같은 영속성 컨텍스트 안에 같은 PK를 통해 가져온 객체는 == 비교 시, JPA는 항상 true를 반환해야 함

 

- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 org.hibernate.LazyInitializationException 발생

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
31
32
33
34
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
 
//            em.detach(refMember);
//            em.close();
            em.clear();
 
            System.out.println("refMember.id = " + refMember.getName());
            // org.hibernate.LazyInitializationException
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

 

 

 Proxy 객체 확인

- Proxy 인스턴스 초기화 여부 확인

    - emf.getPersistenceUnitUtil().isLoaded(refMember);

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
31
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
            System.out.println("isLoaded: " + emf.getPersistenceUnitUtil().isLoaded(refMember)); // false
 
            refMember.getName();
            System.out.println("isLoaded: " + emf.getPersistenceUnitUtil().isLoaded(refMember)); // true
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

 

- Proxy 강제 초기화

    - Hibernate.initialize(refMember);

 

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

[JPA_Basic] Cascade  (0) 2023.09.11
[JPA_Basic] N+1 문제  (0) 2023.09.10
[JPA_Basic] @MappedSuperclass  (0) 2023.09.05
[JPA_Basic] 상속관계 매핑  (0) 2023.08.23
[JPA_Basic] ERD를 기반으로 한 Entity 작성  (0) 2023.08.17

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

@MappedSuperclass

- 주로 등록일, 수정일, 등록자, 수정자 같이 전체 엔티티에서 공통적으로 사용하는 정보를 모을 때 사용

 

📑 특징

- 상속관계 매핑 X

- Entity가 아니므로, 테이블과 매핑되지 않음 ▶ 상속받는 자식 클래스에 매핑 정보만 제공

- 생성해서 사용할 일이 없으므로 abstract class로 사용

 

1. BaseEntity 생성

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
 
@MappedSuperclass
public abstract class BaseEntity {
 
    @Column(name = "INSERT_MEMBER")
    private String createBy;
    private LocalDateTime createDate;
    @Column(name = "UPDATE_MEMBER")
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
 
    public String getCreateBy() {
        return createBy;
    }
 
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
 
    public LocalDateTime getCreateDate() {
        return createDate;
    }
 
    public void setCreateDate(LocalDateTime createDate) {
        this.createDate = createDate;
    }
 
    public String getLastModifiedBy() {
        return lastModifiedBy;
    }
 
    public void setLastModifiedBy(String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }
 
    public LocalDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }
 
    public void setLastModifiedDate(LocalDateTime lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
}
 
 

 

2. BaseEntity 상속

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
 
@Entity
public class Team extends BaseEntity {
 
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;
 
    @OneToMany(mappedBy = "team"// mappedBy: 상대 Entity의 FK field 값
    private List<Member> members = new ArrayList<>();
 
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<Member> getMembers() {
        return members;
    }
 
    public void setMembers(List<Member> members) {
        this.members = members;
    }
 
    public void addMember(Member member) {
        member.setTeam(this);
        this.getMembers().add(member);
    }
}
 
 

* @Entity 클래스는 엔티티나 @MappedSuperclass로 지정한 클래스만 상속 가능

 

🖨️ 실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
Hibernate: 
    
    create table Team (
       TEAM_ID bigint not null,
        INSERT_MEMBER varchar(255),
        createDate timestamp,
        UPDATE_MEMBER varchar(255),
        lastModifiedDate timestamp,
        name varchar(255),
        primary key (TEAM_ID)
    )
 
 

 

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

[JPA_Basic] N+1 문제  (0) 2023.09.10
[JPA_Basic] Proxy  (0) 2023.09.08
[JPA_Basic] 상속관계 매핑  (0) 2023.08.23
[JPA_Basic] ERD를 기반으로 한 Entity 작성  (0) 2023.08.17
[JPA_Basic] 다대다 매핑  (0) 2023.08.16

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

ERD

 

1. JOINED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.persistence.*;
 
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item {
 
    @Id @GeneratedValue
    private Long id;
 
    private String name;
    private int price;
 
}
 
 
 

 

🖨️ 실행 결과

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Hibernate: 
    
    create table Album (
       artist varchar(255),
        id bigint not null,
        primary key (id)
    )
 
Hibernate: 
    
    create table Book (
       ISBN varchar(255),
        author varchar(255),
        id bigint not null,
        primary key (id)
    )
 
Hibernate: 
    
    create table Item (
       DTYPE varchar(31not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
 
Hibernate: 
    
    create table Movie (
       actor varchar(255),
        director varchar(255),
        id bigint not null,
        primary key (id)
    )
 
 
Hibernate: 
    
    alter table Album 
       add constraint FKcve1ph6vw9ihye8rbk26h5jm9 
       foreign key (id) 
       references Item
 
Hibernate: 
    
    alter table Book 
       add constraint FKbwwc3a7ch631uyv1b5o9tvysi 
       foreign key (id) 
       references Item
 
Hibernate: 
    
    alter table Movie 
       add constraint FK5sq6d5agrc34ithpdfs0umo9g 
       foreign key (id) 
       references Item
 
 
 

 

📑 특징

- 장점

    - 외래 키 참조 무결성 제약조건 활용가능

    - 저장공간 효율화

- 단점

    - 조회 시, 조인 사용으로 인한 성능 저하

    - 데이터 저장 시, INSERT Query 2번 실행

 

🖨️ INSERT + ITEM 중심 SELECT 실행 결과

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
31
32
33
34
35
36
37
38
39
40
41
42
Hibernate: 
    /* insert jpa_basic.Movie
        */ insert 
        into
            Item
            (name, price, DTYPE, id) 
        values
            (?, ?, 'Movie', ?)
 
Hibernate: 
    /* insert jpa_basic.Movie
        */ insert 
        into
            Movie
            (actor, director, id) 
        values
            (?, ?, ?)
Hibernate: 
    select
        item0_.id as id2_2_0_,
        item0_.name as name3_2_0_,
        item0_.price as price4_2_0_,
        item0_1_.artist as artist1_0_0_,
        item0_2_.ISBN as isbn1_1_0_,
        item0_2_.author as author2_1_0_,
        item0_3_.actor as actor1_4_0_,
        item0_3_.director as director2_4_0_,
        item0_.DTYPE as dtype1_2_0_ 
    from
        Item item0_ 
    left outer join
        Album item0_1_ 
            on item0_.id=item0_1_.id 
    left outer join
        Book item0_2_ 
            on item0_.id=item0_2_.id 
    left outer join
        Movie item0_3_ 
            on item0_.id=item0_3_.id 
    where
        item0_.id=?
 
 
 

 

 

2. SINGLE_TABLE (DEFAULT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.persistence.*;
 
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item {
 
    @Id @GeneratedValue
    private Long id;
 
    private String name;
    private int price;
 
}
 
 
 

 

🖨️ 실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hibernate: 
    
    create table Item (
       DTYPE varchar(31not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        ISBN varchar(255),
        author varchar(255),
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )
 
 
 

 

📑 특징

- 장점

    - 조회 시, 조인이 필요없으므로 성능이 빠름

    - Query가 단순함

- 단점

    - 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 함

    - 단일 테이블에 모든 것을 저장하므로 테이블이 과하게 커질 수 있음

    ▶ 그로 인해 성능 저하가 발생할 수 있음

 

🖨️ INSERT + ITEM 중심 SELECT 실행 결과

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
Hibernate: 
    /* insert jpa_basic.Movie
        */ insert 
        into
            Item
            (name, price, actor, director, DTYPE, id) 
        values
            (?, ?, ?, ?, 'Movie', ?)
 
Hibernate: 
    select
        item0_.id as id2_0_0_,
        item0_.name as name3_0_0_,
        item0_.price as price4_0_0_,
        item0_.artist as artist5_0_0_,
        item0_.ISBN as isbn6_0_0_,
        item0_.author as author7_0_0_,
        item0_.actor as actor8_0_0_,
        item0_.director as director9_0_0_,
        item0_.DTYPE as dtype1_0_0_ 
    from
        Item item0_ 
    where
        item0_.id=?
 
 
 

 

 

3. TABLE_PER_CLASS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import javax.persistence.*;
 
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn
public abstract class Item {
 
    @Id @GeneratedValue
    private Long id;
 
    private String name;
    private int price;
 
}
 
 
 

 

🖨️ 실행 결과

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
31
32
Hibernate: 
    
    create table Album (
       id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        primary key (id)
    )
 
Hibernate: 
    
    create table Book (
       id bigint not null,
        name varchar(255),
        price integer not null,
        ISBN varchar(255),
        author varchar(255),
        primary key (id)
    )
 
Hibernate: 
    
    create table Movie (
       id bigint not null,
        name varchar(255),
        price integer not null,
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )
 
 
 

 

🖨️ INSERT + ITEM 중심 SELECT 실행 결과

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Hibernate: 
    /* insert jpa_basic.Movie
        */ insert 
        into
            Movie
            (name, price, actor, director, id) 
        values
            (?, ?, ?, ?, ?)
 
Hibernate: 
    select
        item0_.id as id1_2_0_,
        item0_.name as name2_2_0_,
        item0_.price as price3_2_0_,
        item0_.artist as artist1_0_0_,
        item0_.ISBN as isbn1_1_0_,
        item0_.author as author2_1_0_,
        item0_.actor as actor1_4_0_,
        item0_.director as director2_4_0_,
        item0_.clazz_ as clazz_0_ 
    from
        ( select
            id,
            name,
            price,
            artist,
            null as ISBN,
            null as author,
            null as actor,
            null as director,
            1 as clazz_ 
        from
            Album 
        union
        all select
            id,
            name,
            price,
            null as artist,
            ISBN,
            author,
            null as actor,
            null as director,
            2 as clazz_ 
        from
            Book 
        union
        all select
            id,
            name,
            price,
            null as artist,
            null as ISBN,
            null as author,
            actor,
            director,
            3 as clazz_ 
        from
            Movie 
    ) item0_ 
where
    item0_.id=?
 
 
 

 

📑 특징

- 장점

    - Not Null 제약 조건 활용 가능

- 단점

    - 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)

    - 자식 테이블을 통합해서 쿼리하기 어려움