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 전략을 적절히 사용하자.
Leave a comment