1. 객체와 테이블 매핑

@Entity

  • JPA를 사용해 테이블과 매핑할 클래스가 반드시 붙여야 할 어노테이션
  • 기본 생성자 필수(@NoArgsConstructor)

속성

  • name : JPA에서 사용할 엔티티 이름 지정(default는 클래스 이름 그대로)

@Table

  • 엔티티와 매핑할 테이블 지정 어노테이션

속성

  • name : 매핑할 테이블 이름(default는 엔티티 이름)
  • catalog : 데이터베이스 카랄로그 매핑
  • schema : 데이터베이스 스키마 매핑
  • uniqueConstraints(DDL) : unique 제약 조건 생성

2. DB 스키마 자동 생성

  • 애플리케이션 실행 시점에 옵션에 따라 자동 생성
  • 개발 서버에서 사용, 운영 서버에선 사용x

속성

  • create : 기존테이블 삭제 후 다시 생성
  • create-drop : create와 같으나 종료시점에 테이블 drop(테스트에서 테이블을 깔끔이 지우고 싶을 때)
  • update : 변경부분만 반영
  • validate : 엔티티와 테이블의 정상 매핑 확인
  • none : 사용하지 않음

주의

  • 운영 서버에선 create, create-drop, update 사용x
  • 개발 초기 : create, update
  • 테스트 서버(개발 중간,여러 사람이 쓰는) : update, validate -> 이것도 사실 가급적이면 쓰지말자!!!
  • 운영 : validate, none

3. 필드와 컬럼 매핑

@Id
private Long id;

@Column(name = "name")
private String username;

private Integer age;

@Enumerated(EnumType.STRING)
private RoleType roleType;

@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;

@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;

@Lob
private String description;

@Transient
private int tmp;
  • @Column : 컬럼 매핑
  • @Temporal : 날짜 타입 매핑(DATE,TIME,TIMESTAMP) -> 최근엔 LocalDate,- LocalDateTime 사용
  • @Enumerated : enum 타입 매핑
  • @Lob : BLOB, CLOB(String default)
  • @Transient : 필드를 컬럼과 매핑하지 않음

4. 기본 키 매핑

4-1. 어노테이션

  • @Id : 직접 할당

  • @GeneratedValue : 자동 할당

    • IDENTITY : DB에 위임(MYSQL)
    • SEQUENCE : DB 시퀀스 오브젝트 사용(ORACLE)
      • @SequenceGenerator 필요
    • TABLE : 키 생성하는 테이블 사용, DB 가리지 않음
      • @TableGenerator 필요
    • AUTO : 위의 3개 중에 선택

4-2 IDENTITY

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
  • 기본 키 생성을 DB에 위임하는 방식 (MYSQL의 AUTO_INCREMENT)
  • 영속성 컨텍스트에서 관리하려면 기본키가 있어야 하는데, IDENTITY 전략은 DB에 INSERT 쿼리가 실행될 때 기본키를 알 수 있다.
  • 그래서 특이하게 트랜잭션 커밋 시점이 아니라 em.persist() 시점에 INSERT 쿼리가 실행된다.
  • 때문에, 모아서 INSERT 하는 것이 불가능하다.


4-3 SEQUENCE

Member

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
        initialValue = 1, allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
            generator = "MEMBER_SEQ_GENERATOR")
    private Long id;

}


JpaMain

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

System.out.println("Before");
em.persist(member);
System.out.println("member.id = " + member.getId());
System.out.println("After");

tx.commit();

  • Before와 After 사이에 em.persist(member) 한 시점에 MEMBER_SEQ에 다음 값을 호출한다.
  • 즉, 영속성 컨텍스트에 넣으려고 할 때, SEQUENCE 전략이면 DB오브젝트(MEMBER_SEQ)에서 값을 얻어와서 id값에 넣어준다.
  • 그리고 실제 INSERT 쿼리는 위 그림의 After이후의 트랜잭션 커밋 시점에 실행된다.
  • IDENTITY 전략과 달리 쿼리를 모았다가 한 번에 실행시킬 수 있다.

여기서 드는 의문점?

DB에 저장할 때 마다 MEMBER_SEQ를 불러오면 성능상 문제가 있지 않을까?

차라리 IDENTITY 전략처럼 INSERT 쿼리가 persist하는 시점에 실행되는 것이 나은게 아닌가? 라고 의문이 생길 수 있다.

@SequenceGenerator의 allocationsize 속성으로 최적화 할 수 있다.

  • allocationsize의 default는 50이다.
    • MEMBER_SEQ를 호출하면 DB는 50개씩 늘어나고, 우리는 메모리 상에서 1씩 사용한다. 그리고 50개를 다 사용하면 그 때 다시 MEMBER_SEQ를 호출한다.
    • 즉, DB에 미리 올려놓고, 메모리에서 그 갯수만큼 사용하는 옵션이다.

Member 클래스의 allocationsize = 50일 때

JpaMain

Member member1 = new Member();
member1.setUsername("A");

Member member2 = new Member();
member2.setUsername("B");

Member member3 = new Member();
member3.setUsername("C");

System.out.println("==========Before==========");

em.persist(member1);
em.persist(member2);
em.persist(member3);

System.out.println("member1.id = " + member1.getId());
System.out.println("member2.id = " + member2.getId());
System.out.println("member3.id = " + member3.getId());

System.out.println("==========After==========");
tx.commit();

  • call next value for MEMBER_SEQ가 2번 호출되었다.
    • 처음 호출은 1을 확보하기 위한 것이고, 2번째 호출은 처음 호출했을 때 1이 확보되어 50개씩 메모리에서 쓰기로 한 것이 잘못된 게 아닌가라는 측면에서 한 번 더 호출된 것이다.
    • 즉, 처음 member1을 persist할 때 MEMBER_SEQ를 2번 호출하고, 그 다음부턴, DB가 아닌 메모리에서 호출되어 50까지 사용한다.

4-4 TABLE

  • 키 생성하는 테이블을 사용하는 전략
  • SEQUENCE 전략과 비슷
  • 모든 DB에서 적용 가능

Member

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@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;

}


JpaMain

Member member1 = new Member();
member1.setUsername("A");

System.out.println("==========Before==========");

em.persist(member1);
System.out.println("member1.id = " + member1.getId());

System.out.println("==========After==========");
tx.commit();


식별자 전략

  • 식별자를 먼 미래까지 유효한 자연키를 찾긴 쉽지 않기 때문에, 대리키를 사용하는 것이 좋다.
  • 기본키를 int형으로 하는 것도 피하자.
  • IDENTITY, SEQUENCE, TABLE 전략을 적절히 사용하자.

Tags:

Categories:

Date:

Leave a comment