JPA

[JPA] 6. 엔티티 매핑

inuma 2021. 1. 5. 10:21
프로젝트 git url github.com/rtef23/JpaStudy
필요 1. docker
2. docker-compose
프로젝트 정보 mysql : 5.7 - docker image
java : 1.8

 

객체와 테이블 매핑

  • @Entity
    • 해당 annotation이 붙은 클래스는 JPA가 관리하는 엔티티
    • 기본 생성자 필요(public, protected 생성자)
    • final 클래스, enum, interface, inner 클래스에 사용 불가
    • 저장할 필드에 final 사용 불가
    • 속성
      • name : JPA에서 사용할 엔티티 이름을 지정한다.
      • 기본값 : 클래스의 SimpleName을 그대로 사용
      • 다른 패키지에 동일한 클래스가 있는 경우, 별도의 name으로 지정하여 사용한다.
@Entity
public class Member {
  ...
}

 

  • @Table
    • 엔티티와 매핑할 테이블 지정
    • catalog, scheme, uniqueConstraints 속성을 정의할 수 있다.
@Entity
@Table(name = "member")
public class Member {
  ...
}

 

데이터베이스 스키마 자동 생성

  • DDL을 application 실행 시점에서 자동 생성
  • 테이블 중심 => 객체 중심
  • 데이터베이스 방언(Dialect)을 활용하여, 데이터베이스에 맞는 DDL 생성
  • hibernate.hbm2ddl.auto 옵션을 활성화하여 사용
    • create
      • SessionFactory 시작시, 스키마를 삭제하고 다시 생성(Drop + Create)
    • create-drop
      • SessionFactory 종료시, 스키마를 삭제(Drop + Create + Drop)
    • update 
      • SessionFactory 시작시, 객체 구성과 스키마를 비교하여 컬럼 추가 작업을 진행
      • 기존 스키마를 삭제하지 않고 유지
    • validate
      • SessionFactory 시작시, 객체 구성과 스키마를 비교하여 다른 경우 예외를 발생
//persistence.xml
<property name="hibernate.hbm2ddl.auto" value="create"/>

 

  • @Column
    • DDL 생성 시점에서 컬럼별 unique, 길이 제한과 같은 제약(unique, length, nullable, ...)을 줄 수 있다.
    • 컬럼 매핑시 사용하는 annotation
    • insertable
      • insert시 해당 값을 데이터베이스에 같이 입력할지 여부
    • updatable
      • update시 해당 값을 데이터베이스에 같이 수정할지 여부
    • nullable
      • DDL 생성시 not null 제약 조건을 추가
      • insert, update시 null체크도 하게 된다.
    • length
      • 값의 길이 제한
    • columnDefinition
      • 컬럼의 제약 사항을 줄 수 있음
@Entity
@Table("member")
class Member {
  @Column(columnDefinition = "varchar(100) default 'TEST_VALUE'")
  private String test1;
  ...
}
/* 아래와 같은 제약사항이 DDL 생성시 들어가게 된다.
create table member (
  test1 varchar(100) default 'TEST_VALUE',
  ...
) engine=InnoDB
*/

 

  •  @Temporal
    • 날짜 타입 매핑
    • LocalDate, LocalDateTime의 타입의 경우, 해당 annotation을 사용할 필요가 없다.
    • java.sql.Date, java.sql.Time, java.sql.Timestamp의 타입을 사용하는 경우 사용

 

  • @Enumerated
    • enum 타입 매핑
    • EnumType으로 ORDINAL, STRING으로 넣을 수 있다.(기본 값으로는 ORDINAL로 설정되어 있다.)
      • ORDINAL
        • enum에서 해당 값의 index 값을 DB에 저장한다.
        • enum 수정시 순서에 신경을 쓰며 수정을 해야하기 때문에 좋지 않다.
        • DDL 생성시에도 컬럼 타입을 integer로 생성하게 된다.
      • STRING
        • enum에서 해당 값의 name()을 DB에 저장한다.
        • DDL 생성시 컬럼 타입을 varchar로 생성하게 된다.

 

  • @Lob 
    • BLOB, CLOB 매핑
    • 매핑하는 타입이 문자의 경우, CLOB 매핑
    • 매핑하는 타입이 문자가 아닌 경우, BLOB 매핑

 

  • @Transient
    • 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
    • 데이터베이스 저장 X, 조회 X

 

기본 키 매핑

  • 직접 할당
    • @Id만 사용
  • 자동 생성(@GeneratedValue)
    • IDENTITY
      • 기본키 생성 방법을 데이터베이스에 위임, MySQL에서 사용
      • mysql의 auto_increment
      • id를 임의의 값으로 수정하면 안된다.
      • 데이터베이스에 insert SQL을 실행한 이후에 ID 값을 알 수 있음
      • GenerationType.IDENTITY의 경우,  EntityManager.commit() API를 호출하는 시점에 insert 쿼리를
        전송하는 것이 아니라 EntityManager.persist() API를 호출하는 시점에 insert 쿼리를 전송한다.
    • SEQUENCE
      • 데이터베이스 시퀀스 오브젝트 사용, Oracle에서 사용
      • @SequenceGenerator 필요
      • GenerationType.SEQUENCE의 경우, EntityManager.persist() API를 호출하는 시점에서는 DB로 부터 sequence 값을 조회하는 쿼리가 전송된다.
    • TABLE
      • 키 생성용 테이블 사용, 모든 DB에서 사용
      • DB 내부의 트랜잭션을 잡기 때문에 조심해서 사용해야 할듯 함.
/* @GeneratedValue(strategy = GenerationType.AUTO)의 insert 실행시 아래의 쿼리들이 전송된다.
Hibernate: 
    select
        next_val as id_val 
    from
        hibernate_sequence for update
            
Hibernate: 
    update
        hibernate_sequence 
    set
        next_val= ? 
    where
        next_val=?
Hibernate: 
        into
            test
            (name, id) 
        values
            (?, ?)
*/

    • AUTO
      • 방언에 따라 자동 지정
      • hibernate5.4.27 기준 MySQL의 경우, 별도의 sequence 테이블을 생성하는 것이 기본 옵션
  • 권장하는 식별자 전략
    • 기본 키 제약 조건
      • null 아님
      • 유일
      • 불변

 

  • @SequenceGenerator
    • allocationSize의 기본 값은 50으로 설정되어 있다. 
    • initialValue, allocationSize을 이용하여 기본키 할당 성능 향상 방법
      • allocationSize 만큼을 DB에서 미리 올려두어서 해당 서버에서는 allocationSize 범위 내에서는 다시 네트워크를 타지 않고 메모리에서 범위 내 값 안에서 증가하도록 하는 방법
      • 여러 컴포넌트(ex. tomcat)가 있는 경우 각자의 initialValue를 다르게 주면 될 듯
      • WAS가 꺼지게 되는 경우 중간에 ID로 할당받지 않은 빈 sequence가 존재할 듯
@Entity
@Table(name = "test")
@SequenceGenerator(name = "testSequenceGenerator", sequenceName = "TEST_SEQ", initialValue = 1, allocationSize = 100)
public class Test {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "testSequenceGenerator")
  private Long id;
  ...
}


//====총 100번의 persist API 호출====
/*
  최초 persist API 실행시에만 sequence select & update 쿼리가 2번 호출 되었고
  나머지 호출 실행시에는 sequence select & update 쿼리가 호출되지 않는다.
*/
IntStream.rangeClosed(0, 99)
         .forEach((number) -> {
            Test test = new Test();

            test.setName("test-" + number);

            entityManager.persist(test);
          });


/*==== sequence select & update 쿼리====
Hibernate: 
    select
        next_val as id_val 
    from
        TEST_SEQ for update
            
Hibernate: 
    update
        TEST_SEQ 
    set
        next_val= ? 
    where
        next_val=?
*/

 

  • @TableGenerator
    • table에도 auto_increment와 같은 값을 allocationSize 만큼 올려두어 SequenceGenerator와 같이 사용할 수 있다.

'JPA' 카테고리의 다른 글

[JPA] 8. 연관관계 매핑 - 다대일(N : 1)  (0) 2021.01.10
[JPA] 7. 연관관계  (0) 2021.01.06
[JPA] 5. 영속성 컨텍스트 - 2  (0) 2020.12.31
[JPA] 4. 영속성 컨텍스트 - 1  (0) 2020.12.31
[JPA] 3. JPA 기본 api 사용  (0) 2020.12.30