Updated:

1. 개요

관계형 데이터베이스는 상속관계가 존재하지 않는다. 객체에 존재하는 상속 관계를 디비의 슈퍼타입 & 서브타입 관계를 통해 매핑할 수 있는데, 이번에는 상속관계 매핑에 대해 알아보도록 하자.

2. 상속관계 매핑

@Inheritance 애노테이션의 strategy 속성을 통해 조인 전략을 지정할 수 있다. 뿐만 아니라 @DiscriminatorColumn을 통해 타입을 관리하는 컬럼의 이름을, @DiscriminatorValue를 통해 해당 타입의 값을 설정할 수도 있다.

2-1. 조인 전략

@Inheritance(strategy=InheritanceType.JOINED)를 통해 조인 전략을 사용할 수 있다. 조인 전략은 테이블 정규화, 외래키 참조 무결성 제약조건을 활용 가능하다는 장점이 있고, 조인을 사용하게 되므로 성능저하 및 조회쿼리가 복잡하고, 데이터 저장 시 INSERT 쿼리가 두 번 호출된다는 단점이 있다.

2-1-1. 소스 코드

[Item.java]

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
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn 
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;

    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 int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Line 2 : 상속관계 매핑에 조인 전략 사용

Line 3 : 타입 관리를 위한 컬럼 생성

[Album.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@DiscriminatorValue("A")
public class Album extends Item {

    private String artist;

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 A로 저장하도록 설정

[Book.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("B")
public class Book extends Item {

    private String author;

    private String isbn;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 B로 저장하도록 설정

[Movie.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;

    private String actor;

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 M으로 저장하도록 설정

[JpaMain.java]

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
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Album album = new Album();
            album.setName("album1");
            album.setPrice(10000);
            album.setArtist("artist1");
            em.persist(album);

            Book book = new Book();
            book.setName("book1");
            book.setPrice(20000);
            book.setAuthor("author1");
            book.setIsbn("isbn1");
            em.persist(book);

            Movie movie = new Movie();
            movie.setName("movie1");
            movie.setPrice(30000);
            movie.setDirector("director1");
            movie.setActor("actor1");
            em.persist(movie);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

2-1-2. 실행 결과

2-2. 단일 테이블 전략

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)을 통해 단일 테이블 전략을 사용할 수 있다. 단일 테이블 전략은 조회 쿼리가 단순하다는 장점이 있고, 자식 엔티티 컬럼은 모두 null을 허용한다는 단점이 있다.

2-2-1. 소스 코드

[Item.java]

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
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn 
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;

    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 int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Line 2 : 상속관계 매핑에 단일 테이블 전략 사용

Line 3 : 타입 관리를 위한 컬럼 생성

[Album.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@DiscriminatorValue("A")
public class Album extends Item {

    private String artist;

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 A로 저장하도록 설정

[Book.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("B")
public class Book extends Item {

    private String author;

    private String isbn;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 B로 저장하도록 설정

[Movie.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;

    private String actor;

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 M으로 저장하도록 설정

[JpaMain.java]

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
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Album album = new Album();
            album.setName("album1");
            album.setPrice(10000);
            album.setArtist("artist1");
            em.persist(album);

            Book book = new Book();
            book.setName("book1");
            book.setPrice(20000);
            book.setAuthor("author1");
            book.setIsbn("isbn1");
            em.persist(book);

            Movie movie = new Movie();
            movie.setName("movie1");
            movie.setPrice(30000);
            movie.setDirector("director1");
            movie.setActor("actor1");
            em.persist(movie);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

2-2-2. 실행 결과

2-3. 구현 클래스마다 테이블 전략

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)을 통해 구현 클래스마다 테이블 전략을 사용할 수 있다. 구현 클래스마다 테이블 전략은 서브 타입을 명확하게 구분해서 처리하고할 때 효과적이고, not null 제약조건을 사용 가능하다는 장점이 있다. 반면에 여러 자식 테이블을 조회할 때 UNION SQL을 사용하므로 성능이 느리고 자식 테이블을 통합해서 쿼리하기가 어렵다는 단점이 있다. ** 구현 클래스마다 테이블 전략은 사용을 지양하는 것을 추천한다.**

2-2-1. 소스 코드

[Item.java]

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
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn 
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;

    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 int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Line 2 : 상속관계 매핑에 구현 클래스마다 테이블 전략 사용

Line 3 : 타입 관리를 위한 컬럼 생성

[Album.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@DiscriminatorValue("A")
public class Album extends Item {

    private String artist;

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 A로 저장하도록 설정

[Book.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("B")
public class Book extends Item {

    private String author;

    private String isbn;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 B로 저장하도록 설정

[Movie.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;

    private String actor;

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }
}

Line 2 : 타입 관리를 위한 컬럼에 해당 타입의 값을 M으로 저장하도록 설정

[JpaMain.java]

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
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Album album = new Album();
            album.setName("album1");
            album.setPrice(10000);
            album.setArtist("artist1");
            em.persist(album);

            Book book = new Book();
            book.setName("book1");
            book.setPrice(20000);
            book.setAuthor("author1");
            book.setIsbn("isbn1");
            em.persist(book);

            Movie movie = new Movie();
            movie.setName("movie1");
            movie.setPrice(30000);
            movie.setDirector("director1");
            movie.setActor("actor1");
            em.persist(movie);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

2-2-2. 실행 결과

출처 : 자바 ORM 표준 JPA 프로그래밍 - 기본편

Updated:

Leave a comment