JPA

[JPA]기본 키 매핑

수수한개발자 2022. 5. 8.
728x90

 

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();//database 커넥션 하나 받았다 생각하면됨.

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member member = new Member();
            member.setUsername("C");

            em.persist(member);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }

        emf.close();

    }
}

메인 메소드입니다. 

기본키 매핑 어노테이션

@Id

@GeneratedValue

@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id

직접할당 : @Id만 사용

자동생성 @GeneratedValue

             -IDENTITY : 데이터베이스에 위임, MYSQL

             -SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE

                                @SequenceGenerator 필요

             -TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용

                          @TableGenerator 필요

             -AUTO : DB에 따라 자동지정, 기본값

 

AUTO

● @GeneratedValue(strategy = GenerationType.AUTO)

● DB에 따라 자동생성 : ORACLE이면 시퀀스가 생성된다.

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

 

IDENTITY 

@GeneratedValue(strategy = GenerationType.IDENTITY)

기본키 생성을 데이테베이스에 위임

즉, id 값을 null로 하면 DB가 알아서 AUTO_INCREMENT 해준다.
     Ex) MySQL, PostgreSQL, SQL Server DB2 등

 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
//H2
Hibernate: 
    
    create table Member (
       id bigint generated by default as identity,
        name varchar(255),
        primary key (id)
    )
//MySQL
Hibernate: 
    
    create table Member (
       id bigint not null auto_increment,
        name varchar(255),
        primary key (id)
    ) engine=MyISAM

 

 

SEQUENCE 

● @GenerateValue(strategy = GenerationType.SEQQUENCE)

● 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트.

    ex) 오라클 시퀀스

●  오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

시퀀스는 Long으로 생성한다. 10억 이상으로 되었을 때 타입을 바꾸는 게 힘들기 때문에 미래를 생각해 생성할 때 Long으로 생성한다.

 

이렇게 생성하면 hibernate_sequence라고 하이버네이트 기본 시퀀스로 생성이 되는데
보통 개발을 할 때 테이블마다 시퀀스를 따로 관리를 하고 싶어 합니다.
그럴 경우엔 @SequenceGenerator로 객체마다 데이터베이스랑 매핑할 시퀀스 이름을 주시면 됩니다.

@Entity
@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq")
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
    private Long id;
    ...
 }

생성하면 1부터 50까지 생성하게 됩니다. 왜냐하면 @SequenceGenerator 속성에는 다음과 같이 있습니다.

여기서 initiaValueallocationSize가 있는데 allocationSize가 defalut 값이 50 이기 때문입니다.

@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
        initialValue = 1, allocationSize = 50)

@SequenceGenerator 처음 코드와 위의 코드는 같은 결과를 가져옵니다. 하나씩 생성하려면 allocationSize = 1로 설정하시면 됩니다.

 

SEQUENCE 전략은 id 값을 설정하지 않고 generator에 매핑된 Sequence 전략(MEMBER_SEQ)에서 id 값을 얻어옵니다.
해당 sequence는 DB가 관리하는 것이기 때문에 DB에서 id 값을 가져와야 합니다.

여기서 만약 id 값을 가져와야 한다고 가정하면 id 값이 DB에 들어간 이후에 알 수 있을 것입니다

그러면 id 값을 가져올 때마다 DB에서 가져온 pk 값을 객체의 id에 넣어야 하는 과정이 계속 일어날 것이고 이러면 네트워크를 왔다 갔다 해야 되기 때문에 성능 상의 저하가 있을 것입니다.

이와 같은 성능 문제를 해결할 수 있는 것이 allocationSize 속성값(기본값:50)입니다.

1) 이 옵션을 사용하면 next call을 할 때 미리 DB에 50개를 한 번에 올려놓고(DB는 sequence가 51개로 만들어짐) 메모리 상에서 1개씩 사용하게 됩니다.

2) 50개를 모두 사용하면 그때 또 next call을 날려서 다시 50개를 올려놓고(DB에 101개 세팅) 메모리에서 sequence를 가져와  51개부터 사용할 수 있습니다.

ex)

// 1(1로 맞추기 위한 dummy 호출), 51(최적화를 위한 호출)
em.persist(member1); // next call 2번 호출 
em.persist(member2); // MEM
em.persist(member3); // MEM

 

여기서 50보다 큰 수로 설정하면 좋지 않나?라는 질문을 할 수 있는데
이론적으로는 더 큰 수를 설정할수록 성능은 좋아지지만 중간에 애플리케이션을 내리는 시점에 사용하지 않는 seq 값이 날아갑니다. 즉, 중간에 구멍이 생기기 때문에 적당한 50~100 사이로 설정하는 것이 좋습니다.

 

TABLE

@GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")

키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략

 

@Entity
@TableGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        table = "MY_SEQUENCES", //데이터베이스 테이블 이름
        pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    }

특징 

● 장점 : 모든 데이터베이스에 적용가능

● 단점 : 성능

    운영서버에서는 사용하기에 적합하지않다.

    Why? DB 마다 관례로 사용하는 것이 다르기 때문에.

 

@TableGenerator 속성

권장하는 식별자 전략 

기본키 제약 조건

1. null이 아니다.

2. 유일해야한다.

3. 변하면 안된다.

 

● 미래까지 이 조건을 만족하는 자연 키(natural key)를 찾기 어렵다. 대신 대리키/대체키를 사용하도록 하자.

    자연키 (Natural Key)

     -  비즈니스적으로 의미 있는키 ex) 주민등록번호, 전화번호

    대리키/대체키(Generate Value)

     -  비즈니스적으로 상관 없는 키 ex) Generate Value, 랜덤 값 ,UUID 등

예를 들어 개인 정보 보호의 목적으로 DB에 주민등록번호를 저장하지 말라는 조건이 들어온다. 이때, 주민등록번호를 pk로 사용하고 있는 테이블뿐만 아니라 해당 테이블의 pk를 fk로 JOIN 하고 있는 다른 테이블에서도 문제가 생긴다.

 

권장하는 식별자 구성 전략

Long형 + 대체키 + 키 생선전략 사용

1. LongType

2. 대체키 사용, 랜덤,UUID 등 비즈니스랑 상관없는 값 사용

3. AUTO_INCREMENT 또는 SEQUENCE 사용

 

id 값으로 int(Integer) 를 사용 안 하고 Long을 사용하는 이유?

int는 0이 있다.

Integer는 10억까지만 가능하다.

Long은 Integer의 2배지만 애플리케이션 전체로 봤을 때의 영향은 작다고 볼 수 있다.

오히려 10억이 넘어갔을 때 해당 id 값을 타입 변경하는 것이 더 어렵다.

728x90

'JPA' 카테고리의 다른 글

[JPA] 영속성 컨텍스트  (0) 2022.05.11
[JPA] 단방향 연관관계  (0) 2022.05.09
[JPA] 기본 @Anotation 정리  (0) 2022.05.08
[JPA] 데이터베이스 스키마 자동 생성  (0) 2022.05.08
[Spring JPA] JPA란?  (0) 2022.05.08

댓글