7.1 상속 관계 매핑
상속 관계 매핑 - @Inheritance(strategy=infheritanceType.XXX)
- 관계형 데이터베이스는 상속 관계 매핑이 없다.
- 그마나 "슈퍼타입 서브타입 관계"라는 모델링 기법이 객체 상속과 가장 유사한다.
- 상속 관계 매핑: 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑하는 것이다.
슈퍼 타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법
- 조인 전략(JOINED): 각각 테이블로 변환
- ITEM 이라는 테이블을 만들고 데이터를 같이 가져올 때 조인으로 구성한다. (조인 전략은 비즈니스적으로 중요하고 복잡할 때 쓰면 좋다.)
- ex) 앨범 데이터를 추가하면 앨범의 이름, 가격은 ITEM에 들어간다. 아티스트 정보는 앨범 테이블에 들어간다. INSERT 두 번 한다. ITEM과 ALBUM의 PK와 FK가 같으니까 조인해서 가져온다.
- 구분하기 위한 컬럼 "DTYPE"을 둔다.
- 데이터가 정교화되어 있고 제약 조건을 아이템에 걸어서 맞출 수 있다.
- 장점: 테이블 정교화 / 외래 키 참조 무결성 제약 조건 활용 가능 / 저장 공간 효율화
- 단점: 조회 시 JOIN을 많이 이용하고 복잡하다. 데이터 저장 시 INSERT 쿼리 2번 호출 (큰 단점 아니다)
- 단일 테이블 전략(SINGLE_TABLE): 통합된 하나의 테이블로 변환 - 기본값
- 논리 모델을 한 테이블(ITEM)로 합쳐버린다. (단일 테이블 전략은 데이터가 단순할 때 쓰면 좋다.)
- 앨범 / 영화 / 책 여부는 "DTYPE"으로 구분한다. - 운영 상 항상 넣도록 한다.
- 성능이 좋다는 게 최대 장점이다.
- @DiscriminatorColumn을 넣지 않아도 자동으로 추가된다.
- 하나의 테이블에 모두 들어가 있어서 앨범 / 영화 / 책 중 무엇인지 분류가 어렵다.
- 장점: JOIN 이 필요 없어서 일반적으로 조회 성능이 빠르다. 조회 쿼리가 단순하다.
- 단점
- 자식 엔티티가 매핑한 컬럼은 모두 null을 허용한다. (이 부분은 데이터 무결성 입장에서 애매하다.)
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
- 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
- 구현 클래스마다 테이블 전략(TABLE_PER_CLASS): 서브 타입 테이블로 변환
- 각각 테이블을 만들고 이름, 가격 정보를 각자 가지고 있도록 한다.
- @DiscriminatorColumn을 넣어도 사용하지 않는다. 각 테이블마다 따로 존재하기 때문이다.
- 사실 쓰면 안되는 전략이다.
- 장점: 서브 타입을 명확하게 구분해서 처리할 때 효과적이다. NOT NULL 제약 조건 사용 가능하다.
- 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다.(UNION SQL)
- 자식 테이블을 통합해서 쿼리하기 어렵다.
- 새로운 타입이 추가될 때 고칠 부분이 많다.
상속 관련 애너테이션
- @Inheritence(strategy=InheritanceType.XXX)
- @DiscriminatorColumn(name = "DTYPE")
- @DiscriminatorValue("xxx")
Ex 1) 단일 테이블 전략 (디폴트)
- Item클래스 - 아래처럼 상속 타입을 지정하지 않는 경우, 자동으로 "단일 테이블 전략"이 선택된다.
<hide/>
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
- album
<hide/>
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Album extends Item {
private String artist;
}
- movie
<hide/>
package hellojpa;
public class Movie extends Item{
private String director;
private String actor;
}
- book
<hide/>
package hellojpa;
public class Book extends Item{
private String author;
private String isbn;
}
Note) 실행 결과
- Item 테이블에 8개의 데이터가 모두 들어간다.
Ex 2) 조인 전략
- 다음과 같이 상속 타입을 JOINED으로 지정한다.
<hide/>
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
Note) 실행 결과
- id가 똑같이 bigInt로 매핑된다.
- 부모 클래스의 Long id 값이 자식 클래스에도 내려간다.
Ex) 실습
- JpaMain 클래스 수정
<hide/>
package hellojpa;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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{
Movie movie = new Movie();
movie.setDirector("directorA");
movie.setName("바람과 함께 사라지다.");
movie.setActor("actorB");
movie.setPrice(1000);
em.persist(movie);
tx.commit();
}catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close(); // 팩토리를 나중에 닫는다.
}
}
Note) 실행 결과
- INSERT 가 두 번 실행된다. movie, item
- 데이터베이스를 보면 ID의 값이 같다
- 부모 테이블 ITEM의 입장에서 ID는 PK 이다.
- 자식 테이블 MOVIE의 입장에서 ID는 PK 이면서 FK에 해당한다.
Ex) 조회하는 경우
<hide/>
package hellojpa;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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{
Movie movie = new Movie();
movie.setDirector("directorA");
movie.setName("바람과 함께 사라지다.");
movie.setActor("actorB");
movie.setPrice(1000);
em.persist(movie);
em.flush();
em.clear(); // 영속성 컨텍스트에 있는 내용을 DB에 보낸다.
Movie findMovie = em.find(Movie.class, movie.getId());
System.out.println("findMovie " + findMovie);
tx.commit();
}catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close(); // 팩토리를 나중에 닫는다.
}
}
Note) 실행 결과
- item과 inner join해서 movie 정보를 가져온다.
- JPA가 알아서 상속 관계에 JOIN 까지 해준다.
Ex) 조인 전략 - DTYPE 추가
- Item 클래스에 애너테이션을 추가한다.
- name을 안 넣으면 엔티티명이 디폴트이다. 디폴트가 관례 (싱글 테이블 전략에서는 애너테이션을 넣지 않아도 자동 생성)
@DiscriminatorColumn(name ="바꾸고 싶은 이름")
Note) 실행 결과
- Item테이블에 DTYPE 필드가 추가된다.
- Item에 새로운 필드 DTYPE가 추가되고 기본값으로 엔티티명이 들어간다.
Ex) 단일 테이블 전략
- 성능이 좋다.
- 상속 관계를 단일 테이블 전략으로 바꾼다. SINGLE_TABLE
<hide/>
package hellojpa;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public 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;
}
}
Note) 실행 결과
- Item 테이블 하나 생성되고 movie, book, album 등 테이블은 생성되지 않는다.
- insert 쿼리 하나만 날아간다. 조인할 필요도 없다.
Ex 3) 구현 클래스마다 테이블 전략
- 조인 전략과 비슷한데 Item 테이블을 없애고 속성을 모두 앨범/ 북/ 무비로 내린다.
- Item 클래스에 다음 애너테이션 추가하고 Item클래스를 abstract로 바꾼다.
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
Note) 실행 결과
- 조인 전략 / 단일 테이블 전략과 다르게 Item 클래스가 만들어지지 않는다.
- 앨범 / 북 / 영화에 대한 클래스 세 개가 만들어지고 Item 클래스는 생성되지 않는다.
Ex) Item 타입(부모 타입)으로 조회하기
<hide/>
package hellojpa;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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{
Movie movie = new Movie();
movie.setDirector("directorA");
movie.setName("바람과 함께 사라지다.");
movie.setActor("actorB");
movie.setPrice(1000);
em.persist(movie);
em.flush();
em.clear(); // 영속성 컨텍스트에 있는 내용을 DB에 보낸다.
Item item = em.find(Item.class, movie.getId());
System.out.println("item " + item);
tx.commit();
}catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close(); // 팩토리를 나중에 닫는다.
}
}
Note) 실행 결과
<hide/>
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_.author as author1_1_0_,
item0_.isbn as isbn2_1_0_,
item0_.actor as actor1_6_0_,
item0_.director as director2_6_0_,
item0_.clazz_ as clazz_0_
from
( select
id,
name,
price,
artist,
null as author,
null as isbn,
null as actor,
null as director,
1 as clazz_
from
Album
union
all select
id,
name,
price,
null as artist,
author,
isbn,
null as actor,
null as director,
2 as clazz_
from
Book
union
all select
id,
name,
price,
null as artist,
null as author,
null as isbn,
actor,
director,
3 as clazz_
from
Movie
) item0_
where
item0_.id=?
item hellojpa.Movie@4d8539de
- 데이터를 찾을 때 UNION으로 조인해서 찾는다.
7.2. @MappedSuperclass - 매핑 정보 상속
@MappedSuperclass - 매핑 정보 상속
- 객체의 입장에서 공통 매핑 정보가 필요할 때 사용한다. (id, name)
- 객체 입장에서 공통 속성을 상속 받아서 쓴다.
- DB에서는 따로 사용한다.
- 불변인 테이블을 제외하고 대부분의 경우는 필요하다.
@MappedSuperclass - 매핑 정보 상속
- 매핑 정보 상속은 상속 관계 매핑이 아니다.
- 엔티티가 아니고 테이블과 매핑하는 것도 아니다.
- 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공한다.
- 조회, 검색이 불가능 ex) em.find(BaseEntity) 불가능
- 직접 생성해서 사용할 일이 없으므로 추상 클래스로 쓰는 걸 권장한다.
- 테이블과 관계 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할이다.
- 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.
cf)
- @Entity: 상속 관계 매핑
- @MappedSuperClass: 속성만 상속 받을 때, 쓴다.
Ex) MappedSuperClass
- BaseEntity - getter, setter 포함한다.
- 매핑 정보만 받는 부모 클래스라고 보면 된다.
<hide/>
package hellojpa;
import java.time.LocalDateTime;
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public class BaseEntity {
public String createdBy;
private LocalDateTime createdDate;
private String lastModifiedBy;
private LocalDateTime lastModifiedDate;
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public LocalDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(LocalDateTime createdDate) {
this.createdDate = createdDate;
}
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;
}
}
- Team, Member 클래스들이 모두 BaseEntity를 상속 받도록 한다.
- JPA
<hide/>
package hellojpa;
import java.time.LocalDateTime;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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{
Member member = new Member();
member.setUserName("user1");
member.setCreatedBy("kim");
member.setCreatedDate(LocalDateTime.now());
em.persist(member);
em.flush();
em.clear(); // 영속성 컨텍스트에 있는 내용을 DB에 보낸다.
tx.commit();
}catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close(); // 팩토리를 나중에 닫는다.
}
}
Note) 실행 결과
- 상속받은 두 테이블에도 BaseEntity의 네 개의 필드가 새로 생긴다.
7.3 실전 예제 (4) - 상속 관계 매핑
요구 사항 추가
- 상품의 종류는 음반, 도서, 영화가 있고 더 확장될 수도 있따.
- 모든 데이터는 등록일과 수정일이 필수다.
Ex) Single Table strategy
- Album
<hide/>
package jpabook.jpashop.domain;
import javax.persistence.Entity;
@Entity
public class Album extends Item {
private String artist;
private String etc;
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getEtc() {
return etc;
}
public void setEtc(String etc) {
this.etc = etc;
}
}
- Book
<hide/>
package jpabook.jpashop.domain;
import javax.persistence.Entity;
@Entity
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;
}
}
- movie
<hide/>
package jpabook.jpashop.domain;
import javax.persistence.Entity;
@Entity
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;
}
}
- Item 클래스 상속 타입 추가
<hide/>
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public class Item {
@Id @GeneratedValue
@Column(name ="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
Note) 실행 결과
- 다음과 같이 Item 테이블이 생성된다.
<hide/>
package jpabook.jpashop;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import jpabook.jpashop.domain.Book;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
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{
Book book = new Book();
book.setName("JPA");
book.setAuthor("김영한");
em.persist(book);
tx.commit();
}catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close(); // 팩토리를 나중에 닫는다.
}
}
Note) 실행 결과
- Single 테이블 전략이라서 Item 테이블에 한 번에 데이터가 들어간다.
Ex) JOIN strategy
- JOINNED로 바꾼다.
<hide/>
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public class Item {
@Id @GeneratedValue
@Column(name ="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
Note) 실행 결과
- 아까보다 간단한 Item 테이블이 생성된다.
- ITEM_ID가 Item, Movie, OrderItem 테이블에 모두 동일하게 나타난다
- 아이템과 북에 모두 데이터가 들어간다.
- 앨범 북 무비 모두 생성된다.
Ex) TABLE_PER_CLASS
<hide/>
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn
public class Item {
@Id @GeneratedValue
@Column(name ="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
Note) 실행 결과
- Item 테이블이 생성되지 않는다.
====================== JPA SHOP 오류 ======================
- 오류: Table_PER_CLASS에서는 Item 클래스가 생성되면 안되는데 생성된다.
- 원인: 다시 돌려보니까 해결됐다. 여러 번 돌리니까 위의 구문이 안 나온다.
- 정상 실행 결과
<hide/
>C:\Users\Ran\.jdks\corretto-1.8.0_342\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:61791,suspend=y,server=n -javaagent:C:\Users\Ran\AppData\Local\JetBrains\IntelliJIdea2022.2\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\charsets.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\access-bridge-64.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\cldrdata.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\dnsns.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\jaccess.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\jfxrt.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\localedata.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\nashorn.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunec.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunjce_provider.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunmscapi.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\sunpkcs11.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\ext\zipfs.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jce.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jfr.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jfxswt.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\jsse.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\management-agent.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\resources.jar;C:\Users\Ran\.jdks\corretto-1.8.0_342\jre\lib\rt.jar;C:\Users\Ran\Desktop\R\inflearn\JPA프로그래밍_기본편\새 폴더\jpashop\target\classes;C:\Users\Ran\.m2\repository\org\hibernate\hibernate-entitymanager\5.3.10.Final\hibernate-entitymanager-5.3.10.Final.jar;C:\Users\Ran\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Ran\.m2\repository\org\hibernate\hibernate-core\5.3.10.Final\hibernate-core-5.3.10.Final.jar;C:\Users\Ran\.m2\repository\org\javassist\javassist\3.23.2-GA\javassist-3.23.2-GA.jar;C:\Users\Ran\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Ran\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Ran\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Ran\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Ran\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Ran\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.0.4.Final\hibernate-commons-annotations-5.0.4.Final.jar;C:\Users\Ran\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Ran\.m2\repository\net\bytebuddy\byte-buddy\1.9.5\byte-buddy-1.9.5.jar;C:\Users\Ran\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Ran\.m2\repository\com\h2database\h2\1.4.199\h2-1.4.199.jar;C:\Users\Ran\.m2\repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar" jpabook.jpashop.JpaMain
대상 VM에 연결되었습니다, 주소: '127.0.0.1:61791', 전송: '소켓'
9월 07, 2022 2:30:53 오전 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: hello
...]
9월 07, 2022 2:30:53 오전 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {5.3.10.Final}
9월 07, 2022 2:30:53 오전 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
9월 07, 2022 2:30:53 오전 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
9월 07, 2022 2:30:53 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
9월 07, 2022 2:30:53 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [org.h2.Driver] at URL [jdbc:h2:tcp://localhost/~/jpashop]
9월 07, 2022 2:30:53 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=sa}
9월 07, 2022 2:30:53 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
9월 07, 2022 2:30:53 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
9월 07, 2022 2:30:54 오전 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate:
drop table Album if exists
Hibernate:
drop table Book if exists
Hibernate:
drop table Category if exists
Hibernate:
drop table CATEGORY_ITEM if exists
Hibernate:
drop table Delivery if exists
Hibernate:
drop table Member if exists
Hibernate:
drop table Movie if exists
Hibernate:
drop table OrderItem if exists
Hibernate:
drop table ORDERS if exists
Hibernate:
drop table Team if exists
Hibernate:
drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate:
create table Album (
ITEM_ID bigint not null,
name varchar(255),
price integer not null,
stockQuantity integer not null,
artist varchar(255),
etc varchar(255),
primary key (ITEM_ID)
)
Hibernate:
create table Book (
ITEM_ID bigint not null,
name varchar(255),
price integer not null,
stockQuantity integer not null,
author varchar(255),
isbn varchar(255),
primary key (ITEM_ID)
)
9월 07, 2022 2:30:54 오전 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@469d003c] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
9월 07, 2022 2:30:54 오전 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@1d3e6d34] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate:
create table Category (
id bigint not null,
createdBy varchar(255),
createdDate timestamp,
lastModifiedBy varchar(255),
lastModifiedDate timestamp,
name varchar(255),
PARENT_ID bigint,
primary key (id)
)
Hibernate:
create table CATEGORY_ITEM (
CATEGORY_ID bigint not null,
ITEM_ID bigint not null
)
Hibernate:
create table Delivery (
id bigint not null,
createdBy varchar(255),
createdDate timestamp,
lastModifiedBy varchar(255),
lastModifiedDate timestamp,
city varchar(255),
status integer,
street varchar(255),
zipcode varchar(255),
primary key (id)
)
Hibernate:
create table Member (
MEMBER_ID bigint not null,
createdBy varchar(255),
createdDate timestamp,
lastModifiedBy varchar(255),
lastModifiedDate timestamp,
city varchar(255),
name varchar(255),
street varchar(255),
zipcode varchar(255),
primary key (MEMBER_ID)
)
Hibernate:
create table Movie (
ITEM_ID bigint not null,
name varchar(255),
price integer not null,
stockQuantity integer not null,
actor varchar(255),
director varchar(255),
primary key (ITEM_ID)
)
Hibernate:
create table OrderItem (
ORDER_ITEM_ID bigint not null,
createdBy varchar(255),
createdDate timestamp,
lastModifiedBy varchar(255),
lastModifiedDate timestamp,
count integer not null,
orderPrice integer not null,
ITEM_ID bigint,
ORDER_ID bigint,
primary key (ORDER_ITEM_ID)
)
Hibernate:
create table ORDERS (
ORDER_ID bigint not null,
createdBy varchar(255),
createdDate timestamp,
lastModifiedBy varchar(255),
lastModifiedDate timestamp,
orderDate timestamp,
status varchar(255),
DELIVERY_ID bigint,
MEMBER_ID bigint,
primary key (ORDER_ID)
)
Hibernate:
create table Team (
MEMBER_ID bigint not null,
city varchar(255),
name varchar(255),
street varchar(255),
zipcode varchar(255),
primary key (MEMBER_ID)
)
Hibernate:
alter table Category
add constraint FK8tepc1qkmluodspg6tnliwhit
foreign key (PARENT_ID)
references Category
Hibernate:
alter table CATEGORY_ITEM
add constraint FKjip0or3vemixccl6vx0kluj03
foreign key (CATEGORY_ID)
references Category
Hibernate:
alter table OrderItem
add constraint FKk7lmf97wukpquk6d8blxy5neq
foreign key (ORDER_ID)
references ORDERS
Hibernate:
alter table ORDERS
add constraint FKdbs21f1yi0coxy9y0kxw4g9jf
foreign key (DELIVERY_ID)
references Delivery
Hibernate:
alter table ORDERS
add constraint FKh0db7kqr88ed8hqtcqw3jkcia
foreign key (MEMBER_ID)
references Member
9월 07, 2022 2:30:54 오전 org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@464a4442'
Hibernate:
call next value for hibernate_sequence
Hibernate:
/* insert jpabook.jpashop.domain.Book
*/ insert
into
Book
(name, price, stockQuantity, author, isbn, ITEM_ID)
values
(?, ?, ?, ?, ?, ?)
9월 07, 2022 2:30:54 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:tcp://localhost/~/jpashop]
대상 VM에서 연결 해제되었습니다. 주소: '127.0.0.1:61791', 전송: '소켓'
종료 코드 0(으)로 완료된 프로세스
Ex)
- 모든 테이블이 BaseEntity를 상속하도록 만든다.
<hide/>
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item extends BaseEntity {
@Id @GeneratedValue
@Column(name ="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = 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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
- Category 테이블
<hide/>
package jpabook.jpashop.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
@Entity
public class Category extends BaseEntity {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne // 자식 입장에서 부모는 하나
@JoinColumn(name ="PARENT_ID")
private Category parent; // 상위 카테고리
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
@ManyToMany
@JoinTable(name ="CATEGORY_ITEM",
joinColumns = @JoinColumn(name ="CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name = "ITEM_ID")
)
// 중간 테이블을 만든다. 내가 조인하는 건 CATEGORY_ID, 반대편 조인은 ITEM_ID
private List<Item> items = new ArrayList<>();
}
Note) 실행 결과
- 모든 테이블에 (수정날짜 수정자 작성날짜 작성자) 필드가 다 추가된다.
- 아이템과 카테고리의 중간테이블 Category_ITEM에는 네 개의 필드가 추가되지 않는데
- 이래서 중간 테이블 다대다 매핑을 쓰지 말아야한다?
'Spring Framework > [인프런] Java ORM 표준 프로그래밍 - JPA' 카테고리의 다른 글
Chapter 09. 값 타입 (0) | 2022.09.07 |
---|---|
Chapter 08. 프록시와 연관 관계 관리 (0) | 2022.09.07 |
Chapter 06. 다양한 연관관계 매핑 (0) | 2022.09.05 |
Chapter 05. 연관 관계 매핑 기초 (0) | 2022.09.05 |
Chapter 04. 엔티티 매핑 (entity mapping) (0) | 2022.09.04 |