BOOK 3 - 자바 ORM 표준 JPA 프로그래밍(4)
엔티티 매핑
@Entity
- JPA를 사용해서 테이블과 매핑할 클래스는
@Entity어노테이션을 필수로 선언해야 한다. @Entity적용시 주의사항- 기본 생성자가 필수로 있어야 한다.
final클래스,enum,interface,inner클래스에는 사용할 수 없다.- 저장할 필드에
final을 사용하면 안된다.
@Table
-
@Table은 엔티티와 매핑할 테이블을 지정한다.- 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
@Access
- JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
AccessType.FIELD: 필드에 직접 접근한다. 필드 접근 권한이private이어도 접근할 수 있다.AccessType.PROPERTY:Getter를 사용하여 접근한다.
다양한 Field 매핑
@Column: entity 필드와 table 컬럼 매핑name: DB table의 컬럼 이름 지정nullable:not null제약 조건 지정length: 문자 크기 지정nullable과length속성은 DDL 자동 생성때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.- 즉, 스키마 자동 생성 기능을 사용하지 않는다면 위 속성들을 사용하지 않아도 되지만 어플리케이션 개발자가 쉽게 엔티티 제약 조건을 파악할 수 있는 장점이 있다.
@Id: primary key 매핑@Enumerated: 자바의 enum 타입 필드와 매핑EnumType.ORDINAL: enum 순서를 데이터베이스에 저장EnumType.STRING: enum 이름을 데이터베이스에 저장
@Temporal: 자바의 날짜 타입 필드와 매핑TemporalType.DATE:date타입과 매핑TemporalType.TIME:time타입과 매핑TemporalType.TIMESTAMP:timestamp타입과 매핑
@Transient: 데이터베이스와 매핑하지 않는다.- 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
기본 키 매핑
- 기본 키를 어플리케이션에서 직접 할당하는 대신에 데이터베이스가 생성해주는 값을 사용할 수 있다.
- 예를 들어, MySQL의
AUTO_INCREMENT기능
- 예를 들어, MySQL의
- JPA가 제공하는 데이터베이스 기본 키 생성 전략
IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.TABLE: 키 생성 테이블을 사용한다.- MySQL은 시퀀스를 제공하지 않고 Oracle은 시퀀스를 제공하는 대신
AUTO_INCREMENT같은 기능을 지원하지 않으므로IDENTITY와SEQUENCE는 데이터베이스에 의존적이다.
- 자동 생성 전략을 사용하려면
@Id를 선언한 필드에@GeneratedValue를 추가하고 원하는 키 생성 전략을 지정하면 된다.
IDENTITY
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용하는 전략이다.
@GeneratedValue(strategy=GenerationType.IDENTITY)
AUTO_INCREMENT처럼 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있다.- 이 전략을 사용하면 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회한다.
em.persist(user);
System.out.println("user.id = " + user.getId());
// 출력 : user.id = 1
-
persist()를 사용해서 엔티티를 저장한 직후 할당된 식별자 값을 조회한다. -
엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다.
IDENTITY전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달되고, 기본 키 값을 조회한다.- 따라서 이 전략은 쓰기 지연이 동작하지 않는다.
SEQUENCE
-
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.
-
주로 Oracle, PostgreSQL, DB2, H2에서 사용하는 전략이다.
-
다음과 같이 데이터베이스 시퀀스를 만들 수 있다.
CREATE SEQUENCE USER_SEQ START WITH 1 INCREMENT BY 1; -
entity에서 사용할 데이터베이스 시퀀스를 매핑하기 위해
@SequenceGenerator를 사용해서 시퀀스 생성기를 등록한다.@Entity @SequenceGenerator( name="USER_SEQ_GENERATOR", sequenceName="USER_SEQ", initialValue=1, allocationSize=1 ) public class User { ...name: 식별자 생성기 이름sequenceName: 데이터베이스에 등록되어 있는 시퀀스 이름initialValue: 시퀀스 생성시 처음 시작하는 수(DDL 자동 생성시에만 사용됨)allocationSize: 시퀀스 한번 호출에 증가하는 수 (최적화에 사용됨)
-
키 생성 전략과 시퀀스 생성기를 지정한다.
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_SEQ_GENERATOR") -
persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.- 조회한 식별자를 엔티티에 할당한 후 엔티티를 영속성 컨텍스트에 저장한다.
- 이후 트랜잭션 커밋으로 flush가 일어나면 엔티티를 데이터베이스에 저장한다.
SEQUENCE 전략 최적화
-
SEQUENCE전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하여 다음과 같이 데이터베이스와 2번 통신한다.- 식별자를 구하기 위한 데이터베이스 시퀀스 조회
- 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장
-
JPA는 시퀀스에 접근하는 횟수를 줄이기 위해
allocationSize를 사용한다. -
설정한
allocationSize값만큼 한번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다.-
예를 들어
allocationSize값이 50이면 시퀀스를 한번에 50 증가시키고, 1~50 까지는 데이터베이스에 직접 접근하지 않고 메모리에서 식별자를 할당한다. -
시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값 충돌이 일어나지 않는 장점이 있다.
-
-
INSERT 성능이 중요하지 않으면
allocationSize를 1로 설정하면 된다.
TABLE
-
키 생성 전용 테이블을 하나 만들고 이름과 기본 키 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.
- 시퀀스 대신에 테이블을 사용한다는 것만 제외하면
SEQUENCE전략과 내부 동작 방식이 같다.
- 시퀀스 대신에 테이블을 사용한다는 것만 제외하면
-
테이블을 사용하므로 모든 데이터베이스에서 사용할 수 있다.
create TABLE MY_SEQUENCES ( sequence_name varchar(255) not null primary key, next_val bigint )sequence_name: 시퀀스 이름next_val: 시퀀스 값
-
테이블 키 생성기를 등록해야 한다.
@Entity @TableGenerator( name="USER_SEQ_GENERATOR", table="MY_SEQUENCES", pkColumnValue="USER_SEQ", allocationSize=1 )name: 식별자 생성기 이름table: 키 생성 테이블명pkColumnName: 시퀀스 컬럼명valueColumnName: 시퀀스 값 컬럼명
-
키 생성 전략과 테이블 키 생성기를 지정한다.
@GeneratedValue(strategy=GenerationType.TABLE, generator="USER_SEQ_GENERATOR")
TABLE 전략 최적화
TABLE전략은 기본 키 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다.SEQUENCE전략과 비교해서 데이터베이스와 한번 더 통신하는 단점이 있다.
- 데이터베이스 통신 횟수를 줄여 최적화 하기 위해
allocationSize를 사용하고, 최적화 방법은SEQUENCE전략과 동일하다.
AUTO
- 선택한 Database Dialect에 따라
IDENTITY,SEQUENCE,TABLE전략 중 하나를 자동으로 선택한다. - 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 장점이 있다.
@GeneratedValue(strategy=GenerationType.AUTO)
또는
@GeneratedValue