Spring Framework/[인프런] Java ORM 표준 프로그래밍 - JPA

Chapter 07. 고급 매핑

계란💕 2022. 9. 6. 21:45

7.1 상속 관계 매핑

 

상속 관계 매핑 - @Inheritance(strategy=infheritanceType.XXX)

관계형 데이터베이스(논리 모델)와 객체의 상속 관계

  • 관계형 데이터베이스는 상속 관계 매핑이 없다.
  • 그마나 "슈퍼타입 서브타입 관계"라는 모델링 기법이 객체 상속과 가장 유사한다.
  • 상속 관계 매핑: 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑하는 것이다.

 

 

슈퍼 타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법

  1. 조인 전략(JOINED): 각각 테이블로 변환
    • ITEM 이라는 테이블을 만들고 데이터를 같이 가져올 때 조인으로 구성한다. (조인 전략은 비즈니스적으로 중요하고 복잡할 때 쓰면 좋다.)
    • ex) 앨범 데이터를 추가하면 앨범의 이름, 가격은 ITEM에 들어간다. 아티스트 정보는 앨범 테이블에 들어간다. INSERT 두 번 한다. ITEM과 ALBUM의 PK와 FK가 같으니까 조인해서 가져온다.
    • 구분하기 위한 컬럼 "DTYPE"을 둔다.
    • 데이터가 정교화되어 있고 제약 조건을 아이템에 걸어서 맞출 수 있다.
    • 장점: 테이블 정교화 / 외래 키 참조 무결성 제약 조건 활용 가능 / 저장 공간 효율화
    • 단점: 조회 시 JOIN을 많이 이용하고 복잡하다. 데이터 저장 시 INSERT 쿼리 2번 호출 (큰 단점 아니다)
  2. 단일 테이블 전략(SINGLE_TABLE): 통합된 하나의 테이블로 변환 - 기본값
    • 논리 모델을 한 테이블(ITEM)로 합쳐버린다. (단일 테이블 전략은 데이터가 단순할 때 쓰면 좋다.)
    • 앨범 / 영화 / 책 여부는 "DTYPE"으로 구분한다. - 운영 상 항상 넣도록 한다.
    • 성능이 좋다는 게 최대 장점이다.
    • @DiscriminatorColumn을 넣지 않아도 자동으로 추가된다.
      • 하나의 테이블에 모두 들어가 있어서 앨범 / 영화 / 책 중 무엇인지 분류가 어렵다.
    • 장점: JOIN 이 필요 없어서 일반적으로 조회 성능이 빠르다. 조회 쿼리가 단순하다.
    • 단점
      • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용한다. (이 부분은 데이터 무결성 입장에서 애매하다.)
      • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
      • 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
  3. 구현 클래스마다 테이블 전략(TABLE_PER_CLASS): 서브 타입 테이블로 변환
    • 각각 테이블을 만들고 이름, 가격 정보를 각자 가지고 있도록 한다.
    • @DiscriminatorColumn을 넣어도 사용하지 않는다. 각 테이블마다 따로 존재하기 때문이다.
    • 사실 쓰면 안되는 전략이다.
    • 장점: 서브 타입을 명확하게 구분해서 처리할 때 효과적이다. NOT NULL 제약 조건 사용 가능하다.
    • 단점
      • 여러 자식 테이블을 함께 조회할 때 성능이 느리다.(UNION SQL)   
      • 자식 테이블을 통합해서 쿼리하기 어렵다. 
      • 새로운 타입이 추가될 때 고칠 부분이 많다.

 

1. 조인 전략

 

2. 단일 테이블 전략

 

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

 

 

상속 관련 애너테이션

  • @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에는 네 개의 필드가 추가되지 않는데 
    • 이래서 중간 테이블 다대다 매핑을 쓰지 말아야한다?

 

 

 

출처 - https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com