✅ IDENTITY 전략
IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임하는 전략이다. 데이터베이스가 기본 키를 자동으로 생성해준다.
- Entity Class
package jpabook.start; import javax.persistence.*; @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String userName; public Long getId() { return id; } public String getUserName() { return userName; } }
- Main
public class JpaMain { public static void main(String[] args) { //엔티티 매니저 팩토리 생성 EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook"); //엔티티 매니저 생성 EntityManager em = emf.createEntityManager(); //트랜잭션 기능 획득 EntityTransaction tx = em.getTransaction(); try { tx.begin(); //트랜잭션 시작 logic2(em); //비즈니스 로직 tx.commit();//트랜잭션 커밋 System.out.println("============ after commit ==========="); } catch (Exception e) { e.printStackTrace(); tx.rollback(); //트랜잭션 롤백 } finally { em.close(); //엔티티 매니저 종료 } emf.close(); //엔티티 매니저 팩토리 종료 } public static void logic2(EntityManager em) { Board board1 = new Board(); em.persist(board1); System.out.println("============ after board1 flush ============"); // IDENTITY 전략은 commit을 수행하기 전에 영속성 컨텍스트에 저장하기 위해서 id 값이 필요하기 때문에 // flush()가 먼저 수행된다, 즉 지연 쓰기가 적용되지 않는다 System.out.println("board.id: " + board1.getId()); Board board2 = new Board(); em.persist(board2); System.out.println("============ after board2 flush ============"); System.out.println("board2.id: " + board2.getId()); }
IDENTITY 전략은 데이터를 데이터베이스에 INSERT한 후에 기본 키 값을 조회할 수 있다. 따라서 엔티티에 식별자 값을 할당하려면, 즉 영속성 컨텍스트에서 관리하기 위해서는 식별자 값이 반드시 필요하기 때문 JPA는 데이터를 INSERT한 후에 추가로 데이터 베이스에서 해당 데이터를 조회하여 식별자 값을 가져와야 한다. 그래서 쓰기 지연이 동작되지 않는다.
- IDENTITY
commit이 이루어지기 전에 식별자 값이 필요하므로 데이터를 먼저 flush()
해서 식별자 값을 얻어온 쿼리를 볼 수 있다. 쓰기 지연이 동작되지 않은 것이다.
JDBC3에 추가된 Statement.getGeneratedKeys()
를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어올 수 있다. 하이버네이트는 이 메소드를 사용해서 데이터 베이스와 한 번만 통신할 수 있다.
✅ SEQUENCE 전략
SEQUENCE 전략은 데이터베이스의 시퀀스(sequence)를 활용하여 기본 키 값을 생성한다. 시퀀스는 데이터베이스 객체로, 순차적으로 증가하는 일련의 숫자를 생성하고, 데이터베이스 종류에 상관없이 일관성 있는 기본 키 값을 생성할 수 있는 장점이 있다.
- Entity Class
package jpabook.start; import javax.persistence.*; @Entity @SequenceGenerator( name = "BOARD_SEQ_GENERATOR", sequenceName = "BOARD_SEQ", initialValue = 1, allocationSize = 1 ) public class Board { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR") private Long id; private String userName; public Long getId() { return id; } public String getUserName() { return userName; } }
- @SequenceGenerator : 어노테이션을 사용하여 시퀀스 생성기를 정의한다.
- name : 시퀀스 생성기의 이름을 지정한다
- sequenceName : 데이터베이스에서 사용할 시퀀스의 이름을 지정한다
- initalValue : 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한
- allocationSize : 시퀀스에서 한 번에 가져올 값의 개수를 지정한다, 기본값은 50이다. 시퀀스를 호출할 때마다 값이 50씩 증가하며, JPA는 메모리에서 해당 시퀀스를 증가시키며 값을 할당한다. 50의 값을 모두 증가시킬 경우 Database 시퀀스에서 다음 50의 값을 가져오며 다시 메모리에서 증가시키는데 이렇게 동작하는 이유는 DB와의 통신을 최소화하기 위한 최적화 때문이다.
- @SequenceGenerator : 어노테이션을 사용하여 시퀀스 생성기를 정의한다.
- Main Class
public class JpaMain { public static void main(String[] args) { //엔티티 매니저 팩토리 생성 EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook"); //엔티티 매니저 생성 EntityManager em = emf.createEntityManager(); //트랜잭션 기능 획득 EntityTransaction tx = em.getTransaction(); try { tx.begin(); //트랜잭션 시작 logic2(em); //비즈니스 로직 tx.commit();//트랜잭션 커밋 System.out.println("============ after commit ==========="); } catch (Exception e) { e.printStackTrace(); tx.rollback(); //트랜잭션 롤백 } finally { em.close(); //엔티티 매니저 종료 } emf.close(); //엔티티 매니저 팩토리 종료 } public static void logic2(EntityManager em) { Board board1 = new Board(); em.persist(board1); System.out.println("============ after board1 flush ============"); // Sequence 전략은 식별자를 먼저 획득하여 영속성 컨텍스트에서 관리된다 // 이후 commit에 의하여 데이터베이스에 저장되며, 즉 지연 쓰기가 적용된다 System.out.println("board.id: " + board1.getId()); Board board2 = new Board(); em.persist(board2); System.out.println("============ after board1 flush ============"); System.out.println("board2.id: " + board2.getId()); }
SEQUENCE 전략은 persist()
를 호출할 때 먼저 데이터 베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당하며 엔티티를 영속성 컨텍스트에 저장하여 관리한다. 이후에 트랜잭션을 커밋할 때 flush()
가 일어나면 엔티티를 데이터에비스에 저장한다. 앞에서 IDENTITY와는 순서가 조금 다르다.
- IDENTITY : data를 데이터베이스에 저장 → 식별자 획득 → 영속성 컨텍스트에서 관리
- SEQUENCE : 식별자 획득 → 영속성 컨텍스트에서 관리 → 트랜잭션 commit 후 데이터를 데이터베이스에 저장
시퀀스의 현재 값은 다음에 호출될 시퀀스 값을 이야기하고, 증가는 증가되는 크기를 이야기한다. 제일 처음 시퀀스가 호출되었을 때(em.persist(board1);
) ‘1~50'까지의 호출되었고 두 번째 호출될 때(em.persist(board2);
) ‘51~100’까지 호출되었다. 이는 Entity Manager에 flush()
한 시점이 두 번이라는 이야기이며, 각각의 호출 때마다 시퀀스가 부여되었고, 그 크기는 50이다. 따라서 Entity Manager가 실제적으로 관리하는 객체는 총 2개인데 나머지 98개의 시퀀스가 낭비된 것이다. 반복적으로 일정한 개수만큼 commit을 진행하는 경우, 시퀀스가 낭비되지 않게 관리할 수 있는 경우에 유용하다.
✅ TABLE 전략
TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터 베이스의 시퀀스를 흉내내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다. 따라서 특정 데이터 베이스에 종속적이지 않다.
- Entity Class
package jpabook.start; import javax.persistence.*; @Entity @TableGenerator( name = "BOARD_SEQ_GENERATOR", table = "MY_SEQUENCES", //initialValue = 1, pkColumnValue = "BOARD_SEQ", allocationSize = 1 ) public class Board { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR") private Long id; private String userName; public Long getId() { return id; } public String getUserName() { return userName; } }
- @TableGenerator : 테이블 기반의 시퀀스 생성기를 정의한다
- name : 생성기의 이름을 지정한다
- table : 생성기 테이블의 이름을 지정한다
- initialValue : 처음 시작 값을 지정한다
- pkColumnValue : 생성기 식별자 컬럼(pkColumnName)에 저장될 값을 지정한다
- allocationSize : 한 번에 생성되는 기본 키 값의 범위를 지정한다
- @TableGenerator : 테이블 기반의 시퀀스 생성기를 정의한다
- Main
public class JpaMain { public static void main(String[] args) { //엔티티 매니저 팩토리 생성 EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook"); //엔티티 매니저 생성 EntityManager em = emf.createEntityManager(); //트랜잭션 기능 획득 EntityTransaction tx = em.getTransaction(); try { tx.begin(); //트랜잭션 시작 logic2(em); //비즈니스 로직 tx.commit();//트랜잭션 커밋 System.out.println("============ after commit ==========="); } catch (Exception e) { e.printStackTrace(); tx.rollback(); //트랜잭션 롤백 } finally { em.close(); //엔티티 매니저 종료 } emf.close(); //엔티티 매니저 팩토리 종료 } public static void logic2(EntityManager em) { // 하나의 트랜잭션에서 동작하므로 NEXT_VAL 값이 증가하지 않는다. Board board1 = new Board(); em.persist(board1); System.out.println("============ after board1 flush ============"); System.out.println("board.id: " + board1.getId()); Board board2 = new Board(); em.persist(board2); System.out.println("============ after board1 flush ============"); System.out.println("board2.id: " + board2.getId()); }
initialValue 값을 설정하지 않았기 때문에 0부터 값이 증가하여, 0과 1의 값이 Id 값으로 할당되었고 다음 값으론 2가 나타나게 된다. 영속성 컨텍스트에 저장하기 위해서 Table에서 Id의 시퀀스 값을 가져오고, 다시 영속성 컨텍스트에서 관리하는 Entity 클래스를 Database에 저장할 때(commit), 총 2번 데이터 베이스와 통신하는 단점이 있다.
✅ AUTO 전략
선택한 데이터베이스의 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택하는 전략이다. 예를 들어 오라클은 SEQUENCE, MySQL은 IDENTITY를 사용한다.
package jpabook.start;
import javax.persistence.*;
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String userName;
public Long getId() {
return id;
}
public String getUserName() {
return userName;
}
}
Uploaded by N2T