자바 ORM 표준 JPA 프로그래밍 학습 내용 정리한 포스팅 입니다.
JPA 의 매핑 어노테이션
- 객체와 테이블 매핑 :
@Entity
, @Table
- 기본 키 매핑 :
@Id
- 필드와 컬럼 매핑 :
@Column
- 연관관계 매핑 :
@ManyToOne
, @JoinColumn
4.1 @Entity
- 테이블을 매핑할때
@Entity
어노테이션을 필수로 붙어야 한다.
속성 |
기능 |
기본값 |
name |
JPA 에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다. |
설정하지 않으면 클래스 이름을 그대로 사용. |
@Entity 적용시 주의할 점
- 매개변수가 없는 기본 생성자는 필수.
final
, enum
, interface
, inner
클래스에는 사용할 수 없다.
- 저장할 필드에
final
을 사용하면 안된다.
4.2 @Table
@Table
은 엔티티와 매핑할 테이블을 지정한다.
속성 |
기능 |
기본값 |
name |
매핑할 테이블 이름 |
엔티티 이름을 사용. |
catalog |
catalog 기능이 있는 데이터베이스에서 catalog 를 매핑 |
|
schema |
shcema 기능이 있는 데이터베이스에서 schema 를 매핑 |
|
uniqueConstrains(DDL) |
DDL 생성 시 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 이 기능은 스키마 자동생성 기능을 사용해서 DDL 를 만들때만 사용 |
|
4.3 다양한 매핑 사용
4.4 데이터베이스 스키마 자동생성
- JPA 는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원
- 스키마 자동생성 기능을 사용하려면
persistence.xml
에 다음 속성을 추가해야 한다.
- 아래 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성.
- 자동으로 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다.
1
|
<property name="hibernate.hbm2ddl.auto" value="create" />
|
장점
- 개발자가 테이블을 직접 생성하는 수고를 덜 수 있다.
주의사항
- 운영 서버에서
create
, create-drop
, update
처럼 DDL 을 수정하는 옵션은 절대 사용하면 안됨.
hibernate.hbm2ddl.auto 속성
옵션 |
기능 |
create |
기존 테이블을 삭제하고 새로 생성. (DROP + CREATE) |
create-drop |
create 속성에 추가로 어블리케이션을 종료할때 생성한 DDL 제거 |
update |
데이터베이스를 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정 |
validate |
데이터베이스 테이블과 엔티티 매핑정보를 비교. 차이가 있으면 경고를 남기고 어플리케이션을 실행하지 않음. 이 설정은 DDL 을 수정하지 않는다. |
none |
자동생성 기능을 사용하지 않음. (none 은 사실 유요하지 않은 옵션 값) |
개발 환경에 따른 추천 전략
- 개발 초기 단계는 create 또는 update
- 초기화 상태로 자동화된 테스트 진행시 개발자 환경과 CI 서버는 create 또는 create-drop
- 테스트 서버는 update 또는 validate
- 스테이징과 운영 서버는 validate 또는 none
이름 매핑 전략 변경하기
- 단어와 단어를 구분시 자바 언어는 관례상 카멜(Camel) 표기법을 사용하고 데이터베이스는 언더스코어(_) 를 주로 사용하기 때문에 엔티티를 매핑하려면
@Column.name
속성을 명시해야 함.
hiberante.ejb.naming_strategy
속성을 사용하면 이름 매핑 전략을 변경 할 수 있다. 하이버네이트에서는 org.hibernate.cfg.ImprovedNamingStrategy
클래스를 제공.
- 위 클래스는 테이블 명이나 컬럼 명이 생략되면 자바의 카멜 표기법을 테이블의 언더스코어 표기법으로 매핑.
1
|
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
|
4.5 DDL 생성 기능
@Column
매핑정보에 nullable 속성 값을 false 로 지정하여 NOT NULL 제약 조건 추가.
- length 를 지정하면 문자의 크기를 지정할 수 있다.
1
2
3
|
// Member.java 추가
@Column(name = "NAME", nullable = false, length = 10)
private String username;
|
유니크 제약조건
@Table
에 uniqueConstraints
속성을 지정.
1
2
3
4
5
6
|
// Member.java
@Entity
@Table(name = "MEMBER", uniqueConstraints = {
@UniqueConstraint (name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"})
})
public class Member {...}
|
4.6 기본키 매핑
- 데이터베이스마다 기본키를 생성하는 방식이 다르기때문에 JPA 에서는 기본 키 생성전략을 제공한다.
JPA 에서 제공하는 데이터베이스 기본 키 생성 전략
- 직접할당 : 기본 키를 애플리케이션에 직접 입력
- 자동생성 : 대리 키 사용 방식
- IDENTITY : 기본 키 생성을 데이터베이스에 위임
- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본키 할당
- TABLE : 키 생성 테이블 사용.
주의사항
- 키 생성 전략을 사용하려면
persistence.xml
에 hibernate.id.new_generator_mapping=true
속성을 반드시 추가 해야 한다.
- 기존 하이버네이트 시스템을 유지보수 하는 것이 아니라면 반드시 true 로 설정할것.
- true 로 설정하면 키 생성을 최적화하는
allocationSize
속성을 사용하는 방식이 달라진다.
persistence.xml
1
|
<property name="hibernate.id.new_generator_mappings" value="true" /> <!-- 키 생성 최적화 전략 -->
|
기본 키 직접 할당 전략
- 기본키를 직접 할당하려면
@Id
로 매핑하면 된다.
@Id
의 적용 가능 자바 타입
- 자바 기본형
- 자바 Wrapper 형
- String
- java.util.Date
- java.sql.Date
- java.math.BigDecimal
- java.math.BigInteger
1
2
3
4
|
// 기본키직접할당
Board board = new Board();
board.setId("id");
em.persistence(board);
|
IDENTITY 전략
- IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임하는 전략이다.
- 주로
Mysql
, PostgreSQL
, SQL Server
, DB2
에서 사용한다.
- Mysql 의 AUTO_INCREMENT 기능
@GeneratedValue
어노테이션을 사용하고 식별자 생성 전략을 명시한다.
1
2
3
4
5
6
|
@Entity
public class Board {
@Id
@GeneratedValue(starategy = GenerationType.IDENTITY)
private Long id;
}
|
- IDENTIY 전략은 데이터베이스에 INSERT 후에 기본 키 값을 조회할 수 있다.
- JDBC3 의
Statement.getGeneratedKeys()
를 사용하면 데이터를 저장하면서 동시에 생성된 키 값도 얻어 올 수 있다.
- 엔티티가 영속상태가 되려면 반드시 식별자가 필요한데, IDENTITY 전략의 경우 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
SEQUENCE 전략
- 데이터베이스의 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터 베이스 오브젝트이다.
1
2
3
4
5
6
7
8
|
-- SEQUENCE DDL
CREATE TABLE BOARD (
ID BIGINT NOT NULL PRIMARY KEY,
DATE VARCHAR(255)
)
-- 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
|
- SEQUENCE 전략은 em.persist() 를 호출할 때 먼저 데이터베이스 시퀀스를 사용해 식별자를 조회한다.
- 조회한 식별자를 엔티티에 할당한 후 엔티티를 영속성 컨텍스트에 저장
- 트랜잭션 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장
@SequenceGenerator
속성 |
기능 |
기본값 |
name |
식별자 생성기 이름 |
필수 |
sequenceName |
데이터베이스에 등록되어 있는 시퀀스 이름 |
hibernate_sequence |
initialValue |
DDL 생성 시에만 사용. 시퀀스 DDL 을 생성할 때 처음 시작하는 수를 지정 |
1 |
allocationSize |
시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용) |
50 |
catalog, schema |
데이터베이스 catalog, schema 이름 |
|
SequenceGenerator.allocationSize
- SequenceGenerator.allocationSize 기본값이 50 이다.
- JPA 는 시퀀스에 접근하는 횟수를 줄이기 위해 allocationSize 사용한다.
- 여기에서 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다.
- 이방법은 시퀀스 값을 선점하므로 JVM 이 동시에 접근해서 데이터를 등록시 시퀀스 값이 한번에 증가하는 점을 염두.
- INSERT 성능이 중요하지 않거나 시퀀스가 많이 증가하는 게 부담될 경우 allocationSize 를 1로 설정한다.
TABLE 전략
- TABLE 전략은 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 만들어 데이터 시퀀스처럼 사용하는 전략이다.
- 이 전략은 모든 데이터베이스에 적용이 가능하다.
1
2
3
4
5
6
|
-- TABLE 전략 시퀀스 테이블 DDL
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
|
@TableGenerator
속성 |
기능 |
기본값 |
name |
식별자 생성기 이름 |
필수 |
table |
키생성 테이블명 |
hibernate_sequence |
pkColumnName |
시퀀스 컬럼명 |
sequence_name |
valueColumnName |
시퀀스 값 컬럼명 |
next_val |
initialValue |
초기 값, 마지막으로 생성된 값이 기준이다 |
0 |
allocationSize |
시퀀스 한 번 호출에 증가하는 수 (성능최적화 이용) |
50 |
catalog, schema |
데이터베이스 catalog, schema 이름 |
|
uniqueConstrains(DDL) |
유니크 제약 조건을 지정할 수 있다. |
|
AUTO 전략
GenerationType.AUTO
는 선택한 데이터베이스 방언에 따라 IDENTITY
, SEQUENCE
, TABLE
전략 중 하나를 자동으로 선택.
- AUTO 는 데이터베이스를 변경해도 코드를 수정할 필요가 없는 장점이 있다.
기본 키 매핑 정리
- 영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 반드시 식별자 값이 있어야 한다.
권장하는 식별자 전략
- null 값은 허용하지 않는다.
- 유일해야 한다.
- 변해서는 안된다.
테이블의 기본 키를 선택하는 전략은 크게 두 가지가 있다.
- 자연키 (natural key) : 비즈니스에 의미가 있는 키. ex) 주민등록번호, 이메일, 전화번호
- 대리키 (surrogate key) : 비즈니스와 관련없는 임의로 만들어진 키. ex) 오라클 시퀀스, auto_increment, 키생성 테이블
- 자연키보다는 대리 키를 권장한다.
4.7 필드와 컬럼 매핑 : 레퍼런스
@Column
@Column
은 객체 필드를 테이블 컬럼에 매핑한다.
- 자바 기본 타입에
@Column
을 사용하면, nullable=false
로 지정하는 것이 안전하다.
속성 |
기능 |
기본값 |
name |
필드와 매핑할 테이블의 컬럼 이름 |
객체의 필드 이름 |
nullable(DDL) |
null 값의 허용 여부 설정. false 로 설정시 not null 제약조건 |
true |
unique(DDL) |
한 컬럼에 간단한 유니크 제약조건을 걸 때 사용 |
|
columnDefinition(DDL) |
데이터베이스 컬럼 정보를 직접 줄 수 있다. |
|
length(DDL) |
문자 길이 제약조건, String 타입에만 사용 |
255 |
precision, scale(DDL) |
BigDecimal/BigInteger 타입에서 사용. precision 은 소수점을 포함한 전체 자리수, scale 은 소수의 자리수 |
precision = 19, scale = 2 |
@Enumerated
- 자바의
enum
타입을 매핑할 때 사용한다.
EnumType.ORDINAL
: enum 에 정의된 순서대로 데이터베이스에 저장된다.
- 장점 : 저장되는 데이터 크기가 작다.
- 단점 : 이미 저장된 enum 순서를 변경할 수 없다.
EnumType.STRING
: enum 이름을 데이터베이스에 저장. (권장)
- 장점 : 저장된 enum 순서가 바뀌거나 enum이 추가되어도 안전
- 단점 : 데이터베이스에 저장되는 크기가 ORINAL 에 비해 크다.
@Temporal
- 날짜 타입 (
java.util.Date
, java.util.Calendar
) 을 매핑할 때 사용.
TemporalType.DATE
, TemporalType.TIME
, TemporalType.TIMESTAMP
@Lob
- 데이터베이스의 BLOB, CLOB 타입과 매핑된다.
@Lob
에는 속성을 지정할 수 없으므로, 매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB 으로 매핑한다.
- CLOB : String, char[], java.sql.CLOB
- BLOB : byte[], java.sql.BLOB
1
2
3
4
5
|
@Lob
private String lobString; // CLOB
@Lob
private byte[] lobByte; // BLOB
|
@Transient
- 데이터베이스에 저장하지도 조회하지도 않는다. 객체에 어떤 값을 임시로 보관하고 싶을 때 사용한다.
@Access
- JPA 가 엔티티 데이터에 접근하는 방식을 지정
- 필드접근 :
AccessType.FIELD
로 지정한다. 필드에 직접 접근하며, 필드 접근 권한이 private 이어도 접근 가능하다.
- 프로퍼티 접근 :
AccessType.PROPERTY
로 지정. 접근자를 사용한다.