[JPA] 상속관계 매핑
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();
}
}
Leave a comment