지나공 : 지식을 나누는 공간

JPA 4 : @GeneratedValue 기본키 매핑 전략 정리 본문

Tech/JPA

JPA 4 : @GeneratedValue 기본키 매핑 전략 정리

해리리_ 2020. 10. 14. 21:20

필수 어노테이션정리

 

@Entity

   -> JPA를 사용해서 데이터베이스의 테이블과 매핑할 클래스에 필수로 명시해야 하는 어노테이션

 

  • 제약사항

1. 파라미터가 없는 protected나 public의 기본생성자가 필수로 있어야 함

   (Entity를 JpaRepository에서 가져올 때 기본생성자를 사용하므로)

2. 필드에 final 클래스, enum, interface, inner 클래스를 사용할 수 없음

 

  • 속성

    name : 엔티티 이름을 지정하는 속성, 기본값은 클래스이름

Team이라는 테이블에 매핑될 Team 객체

 

 

@Table

   -> 해당 엔티티와 매핑할 테이블을 지정하는 어노테이션

 

  • 속성
    name : 매핑할 테이블 이름을 지정, 기본값은 엔티티 이름
    (아래 사진에서 ORDERS라고 지정했지만 안 써도 테이블명이 ORDERS다.)
    catalog : 데이터베이스 catalog 매핑
    schema : 데이터베이스 schema 매핑
    uniqueConstraints : DDL 자동생성 시에 유니크 제약조건 생성
    예) @Table(unqueConstraints = {@UniqueConstraints(name = "NAME_AGE_UNIQUE", columnNames = {"NAME","AGE"} ) } )

엔티티명 = (기본값)클래스이름 = Order, Table명 ORDERS, name속성을 안 줘도 그냥 엔티티명이 Member라면 테이블명이 MEMBER다.

 

@Column

컬럼매핑 어노테이션으로, 필요한 내용을 속성으로 지정할 수 있다.

 

  • 속성
    name : 필드와 매핑할 테이블의 컬럼이름, 기본값이 객체의 필드명이므로 테이블의 컬럼명과 다를 때만 사용- - -insertable, updatable : 추가,변경 가능 여부, 기본값은 true
    nullable : null 허용여부 설정, DDL생성시에 적용되고 이걸 false로 하면 DDL 생성 시 not null 제약조건이 붙는다.
    unique : @Table의 속성인 uniqueConstraints와 같지만 한 컬럼에 대한 유니크 제약을 걸 때 사용columnDefinition : 데이터베이스 컬럼정보를 직접 주고 싶을 때 사용, varchar(100) default 'EMPTY'같은 것
    length : 문자길이 제약조건으로 String 타입에서만 사용
    precision, scale : 멤버타입이 BigDecimal이나 BigInteger일 때 precision은 소수점을 포함한 전체 자릿수, scale은 소수의 자릿수 지정, double이나 float 타입에는 적용이 안되고 큰 숫자나 정밀한 소수를 다룰 때 사용

 

@Id

테이블에서 Primary Key(기본키)가 되는 컬럼을 지정하는 어노테이션

 

@Enumerated(EnumType.STRING)

객체에서는 Enum.Type이 있지만 DB에는 없으므로 자바의 EnumType을 데이터베이스와 매핑할 때 쓰는 어노테이션

 

  • 제약사항
    1. ORDINAL 타입은 사용하지 않도록!
    - ORDINAL 타입으로 하면 enum의 순서값을 데이터베이스에 저장
    - STRING 타입으로 하면 enum의 이름을 데이터베이스에 저장

 

@Temporal

날짜 타입을 매핑할 때 사용하는 어노테이션

 

  • 속성
    value : TemporalType.DATE 이면 date타입(2020-10-14)과 매핑, TIME이면 time타입(11:11:11)과 매핑, TIMESTAMP이면 timestamp타입(2020-10-14 11:10:10)과 매핑

 

@Lob

데이터베이스의 BLOB, CLOB와 매핑하는 어노테이션

속성없음, 매핑하는 필드타입이 문자면 CLOB(String, char, java.sql.CLOB)를, 나머지는 BLOB(byte[], java.sql.BLOB) 매핑

 

@Transient

필드매핑하지 않고 데이터베이스에 저장이나 조회도 하지 않는 멤버에 쓰는 어노테이션

- 주로 메모리상에서만 임시로 보관하고 싶을 때 사용

 


기본키 매핑 어노테이션 @Id에 대하여

 

직접 할당할 땐 @Id만 사용하고, @Id가 선언된 필드에 기본키 값을 자동으로 할당할 때 @GeneratedValue 사용

자동생성은 객체 생성 뒤 직접 코드로 setId()안해줘도 JPA가 알아서 값을 id에 넣어준다는 것이다.

 

@GeneratedValue 전략 (사용예 : @GeneratedValue(starategy = GenerationType.IDENTITY)

 

  • IDENTITY 전략의 특징

    - 기본키 생성을 데이터베이스에 위임하고 주로 MySQL, SQL Server, DB2에서 사용
    - 예를 들자면 MySQL을 사용한다면 auto_increment로 지정해서 DDL을 만든다.

    1차 캐시에는 @Id와 @Entity로 지정한 것들이 들어간다. 그런데 기본키의 GeneratedValue가 IDENTITY 타입이면 id생성을 데이터베이스에게 위임하기 때문에, JPA는 1차 캐시에 넣을 때 id 필드가 뭔지 알 수 없다. 따라서, 보통 JPA는 트랜잭션 커밋시점에 insert문을 실행하지만 IDENTITY일 때는 보통과 달리 persist()를 호출하는 시점에 데이터베이스에 insert문을 날린다. 이래야 1차 캐시(영속성컨텍스트) 에 올릴 때 JPA가 id를 알고 올릴 수 있으니까. 그래서 이렇게 persist를 호출하여 영속성컨텍스트에 올리는 시점에 쿼리문을 날려서 auto_increment가 된 id값을 알고 1차캐시에 @Id를 올린다. 뒤에서 다시 적겠다.

  • SEQUENCE 전략의 특징

    - 시퀀스오브젝트를 만들고 이걸 통해 id 값을 가져온다. (오라클만 가능!!)
    - 데이터베이스 시퀀트 오브젝트를 사용하고, 시퀀스를 지원하는 oracle의 경우에 사용
    - @SequenceGenerator 필요

    SEQUENCE전략에서 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다. 예로는 오라클 시퀀스가 있다.


    - @SequenceGenerator 속성

    name : 식별자 생성기의 이름, 필수 속성이다.
    sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름
    initialValue : DDL 생성 시에만 사용되고 DDL을 생성할 때 처음 시작하는 수를 지정하는 속성
    allocationSize : 시퀀스를 한번에 몇 개 호출하는 지 지정하는 속성, 성능 최적화를 할 때 사용한다. 기본값이 50이므로 데이터베이스에서 시퀀스 값이 몇 씩 증가하는 지 설정에 맞게 이 값을 지정해야 한다.
    catalog, schema : 데이터베이스 카탈로그와 스키마 이름 지정

    allocationSize를 쉽게 설명하자면, 기본값이 50으로 되어있다면 next call을 할 때 미리 시퀀스 50개를 메모리에 쌓아놓고 이걸 다 쓰면 다시 51부터 100까지 다시 메모리에 50개를 쌓는 것이다. 처음에 50개를 쌓아놨으면 그 다음부터는 DB에서 직접 시퀀스를 호출하지 않고 메모리에서 시퀀스를 호출한다. 

  • AUTO 전략의 특징

    방언에 따라 자동으로 지정된다. AUTO가 GeneratedValue의 기본값이다.
    (방언은 SQL 표준을 지키지 않거나 특정 데이터베이스만의 고유 기능을 의미, 각 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다름) 예를 들어 오라클이라면 시퀀스를 만들어서 해줄 것이다.

  • TABLE 전략의 특징

    - 키 생성용 테이블을 만들어서 데이터베이스 시퀀스를 흉내내는 전략
    - @TableGenerator 필요
    - 모든 데이터베이스에 적용할 수 있지만 성능이 좋지 않다는 단점이 있다.

    - 속성
    name : SequenceGenerator의 이름
    table : 생성할 시퀀스테이블의 이름
    initValue : default는 0이다. 이게 0이면 기본키는 1부터 할당된다. initValue가 1이면 2부터 기본값에 할당된다.
    pkColumnValue : 기본키 필드의 이름 ( 아래 예시에서 확인할 것)
    allocationSize : 한 번 시퀀스를 호출할 때마다 시퀀스값이 몇 증가하는지 정하는 속성

    -예시 (예시가 이해하기 가장 빠르니까 붉은색!)

    1. 아래 보이는 것과 같이 엔티티의 기본키 매핑전략을 TABLE로 설정했다.
    initValue는 기본값이 0이지만 1로 설정했으므로 시퀀스값이 2부터 할당될 것이다.
    allocationSize는 기본값이 50이지만, 데이터베이스 시퀀스값이 하나씩 증가하도록 설정되어 있으면 이걸 반드시 1로 설정해야 한다. 지금 나는 데이터베이스 시퀀스값이 하나씩 증가하도록 되어있으므로 이 값을 1로 설정했다.

기본키 매핑전략을 지정한 엔티티

 

       2. 객체를 4개 생성해서 넣고 기본키 값을 확인해보자. 그 전에, MEMBER_SEQ 테이블이 생성되었음을 볼 수 있다.

 

멤버 테이블의 기본키가 자동으로 할당되었다. 2부터 1씩 증가하며.

       3. 이제 만들어진 시퀀스 테이블을 보자. MEMBER테이블의 마지막 기본키값, 즉 현재 값인 5를 가지고 있다. 

          위에서 pkColumnValue로 설정했던 "MEMBER_SEQ"는 아래와 같이 보여진다.

 

생성된 시퀀스테이블의 모습

  • Identity 전략 심화


    identity전략은 기본키값을 우리가 할당하지 않은 채로 DB가 날아오면 그 때 기본키 값을 세팅해준다. 

    그런데, 앞에서 언급했다시피 JPA는 persist를 호출하는 순간 영속성컨텍스트에 데이터를 올리고, 마지막에 commit()을 호출하는 시점에 DB에 쿼리를 날린다. 그렇게 되면 identity는 DB에 값이 들어가봐야 기본키값을 알 수 있는건데 DB에 값을 넣어보기 전에, 즉 commit을 하기 전에, persist()를 호출하므로 JPA는 id도 모른 채로 이 데이터를 영속성 컨텍스트에 올리고 싶어한다. 

    그래서 예외적으로 persist()를 호출하는 시점이 원래는 영속성컨텍스트에는 올리고 DB에는 쿼리를 날리지 않는 시점이지만 identity 전략에서만 예외적으로! persist()시점에 insert 쿼리문을 DB에 날린다.

    sequence를 사용한다면 identity 타입일 때와 조금 다르다. 시퀀스 전략에서는 실제로 commit하는 시점에 쿼리문을 날린다. 왜냐하면, persist()를 호출할 때 살짝 시퀀스의 id값만 얻어올 수 있기 때문에 영속성 컨텍스트에 문제 없이 올릴 수 있기 때문이다. 그래서 이 경우는 보통 때와 같이 commit()시점에 insert쿼리문을 날린다.

 

출처 : 김영한 님의 ORM 표준 JPA 프로그래밍 강의를 수강하고 정리한 내용입니다,

728x90
Comments