JPA는 다양한 매핑 어노테이션들을 제공한다
해당 어노테이션이 붙은 클래스는 JPA가 관리하는것이다
⚠️ 주의사항
엔티티와 매핑할 테이블을 지정한다
name: 매핑할 테이블 이름
catalog: catalog 기능이 있는 데이터베이스에서 catalog 매핑
schema: schema 기능이 있는 DB에서 schema 매핑
uniqueConstraints(DDL): DDL 생성시에 유니크 제약조건을 만듬.
2개이상의 복합 유니크 제약조건 생성가능
스키마 자동 생성기능을 사용해서 DDL을 만들때만 사용
JPA는 매핑정보와 데이터베이스 방언을 사용하여 데이터베이스 스키마를 생성한다.
// persistence.xml 에 추가
<property name="hibernate.hbm2ddl.auto" value="create"/>
// 스키마 자동 생성
위 속성을 추가하면 어플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.
hibernate.show_sql
속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다
hibernate.hbm2ddl.auto 속성
create: # create
create-drop: create 속성에 추가로 어플리케이션을 종료할 때 생성한 DDL을 제거
(DROP + CREATE + DROP)
update: 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정
validate: 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 어플리케이션을 실행하지 않는다. DDL수정X
none: 자동 생성기능을 사용하지 않는다
⚠️ 주의사항
👍 참고사항
@Column(name="role_type")
과 같이 매핑해주면 된다@Entity
@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint(
name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"} )})
public class Member {
@Id
@Column(name="id")
private String id;
@Column(name = "name", nullable = false, length = 10)
private String username;
}
위 기능들은 DDL을 자동으로 생성할 때에만 사용된다 !!
DDL을 직접 만든다면 사용할 이유가 없지만 개발자가 엔티티만 보고도
손쉽게 제약조건을 파악할 수 있다는 장점이 존재한다.
기본 키 생성 전략은 두가지가 있다
데이터베이스 벤더마다 지원하는 방식이 다르기 때문에 자동생성 전략 또한 다양하다 !!
기본 키를 직접 할당하려면 @Id 만 사용하고, 자동 생성 전략을 사용하려면 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다 !
⚠️ 주의사항
키 생성 전략을 사용하려면 persistence.xml
에 hibernate.id.new_generator_mapping = true
속성을 반드시 추가해야한다
이 옵션을 true 로 설정하면 키 생성 성능을 최적화하는 allocationSize 속성을 사용하는 방식이 달라진다 (뒤에서 설명)
@Id
@Column(name = "id")
private String id;
적용 가능 타입은 아래와 같다
Board board = new Board();
board.setId("id");
em.persist(board);
em.persist() 로 엔티티를 저장하기 전에 어플리케이션에서 기본 키를 직접 할당하는 방법
👍 참고사항
기본 키 직접 할당 전략에서 식별자 값 없이 저장하면 예외가 발생. 하이버네이트를 구현체로 사용하면 JPA 최상위 예외인 javax.persistence.PersistenceException
이 발생, 내부에 하이버네이트 예외인 org.hibernate.id.identifierGenerationException
예외를 포함하고 있다.
기본 키 생성을 데이터베이스에 위임하는 전략
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
이 전략을 사용하면 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회한다.
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
// board.id = 1
위 코드를 보면 em.persist() 를 호출해서 엔티티를 저장한 직후에 할당된 식별자 값을 출력
출력된 값 1은 저장 시점에 데이터베이스가 생성한 값을 JPA가 조회한 것
👍 참고사항
IDENTITY 전략은 데이터를 데이터베이스에 INSERT 한 후에 기본 키 값을 조회할 수 있다.
즉, 엔티티에 식별자 값을 할당하려면 JPA는 추가로 데이터베이스를 조회해야한다 !
but, JDBC3에 추가된 Statement.getGeneratedKeys()
를 사용하면 데이터를 저장함과 동시에 생성된 기본 키 값도 얻어올 수 있어 한번만 통신하면 된다 !
⚠️ 주의사항
엔티티가 영속상태가 되려면 식별자가 필요!
하지만 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기전략에서 동작하지 않는다 !
시퀀스를 사용해서 기본 키를 생성
주로 Oracle, PostgreSQL, DB2, H2 에서 사용
@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;
BOARD_SEQ_GENERATOR
라는 시퀀스 생성기 등록BOARD_SEQ
를 지정, JPA는 실제 데이터베이스의 BOARD_SEQ
와 매핑GenerationType.SEQUENCE
로 설정, generator = "BOARD_SEQ_GENERATOR"
로 시퀀스 생성기 선택BOARD_SEQ_GENERATOR
시퀀스 생성기가 할당IDENTITY 전략과 같지만 내부 동작방식은 다르다.
간단하게 설명하면 IDENTITY
는
데이터베이스에 저장 → 식별자 할당 → 식별자 조회 순이고
SEQUENCE
같은 경우
데이터베이스 시퀀스를 사용하여 식별자 조회 → 식별자 할당 → 트랜잭션 커밋 & 플러시 → 데이터베이스에 저장한다.
@SequenceGenerator
name: 식별자 생성기 이름
seqeunceName: 데이터베이스에 등록되어있는 시퀀스 이름
initialValue: DDL 생성시에만 사용
시퀀스 DDL을 생성할 때 처음 시작하는 수 지정
allocationSize: 시퀀스 한 번 호출에 증가하는 수
(성능 최적화에 사용)
catalog, schema: 데이터베이스 catalog, schema 이름
// 매핑할 DDL
create sequence [sequecneName]
start with [initialValue] increment by [allocationSize]
⚠️ 주의사항
SequenceGenerator.allocationSize의 기본 값이 50인 것에 주의해야 한다. JPA가 기본으로 생성하는 데이터베이스 시퀀스는 최적화의 이유로 호출마다 값이 50씩 증가한다. 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다 !!!
위 글을 참고하자 ..!
키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략
모든 데이터베이스에 적용이 가능하다
@Entity
@TableGenerator (
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue (strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
@TableGenerator 를 사용하여 테이블 키 생성기를 등록, @GeneratedValue.generator에 키 생성기 지정. Table 전략은 Sequence 대신에 테이블을 사용한다는 것만 제외하면 Sequence 전략과 내부 동작 방식이 같다
@TableGenerator
name: 식별자 생성기 이름
table: 키 생성 테이블명
pkColumnName: 시퀀스 컬럼명
valueColumnName: 시퀀스 값 컬럼명
pkColumnValue: 키로 사용할 값 이름
initialValue: 초기 값, 마지막으로 생성된 값이 기준
allocationSize: 시퀀스 한 번 호출에 증가하는 수
(성능 최적화에 사용)
catalog, schema: 데이터베이스 catalog, schema 이름
uniqueConstraints(DDL): 유니크 제약 조건 지정
👍 참고사항
Table 전략은 Sequence 전략과 비교해서 데이터베이스와 한번 더 통신하는 단점 존재.
이를 최적화 하려면 allocationSize를 사용하면 되며, Sequence 전략과 방법이 같다.
GenerationType.AUTO 는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
Auto 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것!
👍 참고사항
데이터베이스 기본 키는 3가지 조건을 만족해야함
테이블의 기본 키를 선택하는 전략
비지니스 환경은 언젠가 변하기 때문에 자연키보다는 대리키를 사용하자 !!
ex) 주민번호 자연키를 사용하다가 개인정보 보호법 등의 법적 문제가 생기면 키로 이용하지 못한다. 엄청난 양의 수정이 필요하게 됨
기본적인 원칙으로 기본 키는 변하면 안 된다 !!
필드와 컬럼 매핑 분류
@Column: 컬럼 매핑
@Enumerated: 자바의 Enum 타입 매핑
@Temporal: 날짜 타입 매핑
@Lob: BLOB, CLOB 타입 매핑
@Transient: 특정 필드를 데이터베이스에 매핑하지 않음
@Access: JPA가 엔티티에 접근하는 방식 지정
객체 필드를 테이블 컬럼에 매핑
name: 필드와 매핑할 테이블의 컬럼 이름
insertable: 엔티티 저장 시 이 필드도 같이 저장
updatable: 엔티티 수정 시 이 필드도 같이 수정
table: 하나의 엔티티를 두개 이상의 테이블에 매핑할 때 사용
nullable: null 값 허용 여부 설정
unique(DDL): 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
2개 이상의 유니크 제약조건은 @Table.uniqueConstraints를 사용해야한다
columnDefinition(DDL): 데이터베이스 컬럼 정보를 직접 줄 수 있음
length(DDL): 문자 길이 제약조건, String type에만 사용
precision, scale(DDL): BigDecimal 타입에서 사용
👍 참고사항
@Column을 생략하게 되면 아래와 같은 차이가 있다.
int data;
→ data integer not null
Integer data;
→ data integer
@Column
int data
→ data integer
value: EnumType.ORDINAL : enum 순서를 데이터베이스에 저장 (0부터 시작)
EnumType.STRING : enum 이름을 데이터베이스에 저장
⚠️ 주의사항
ORDINAL은 데이터베이스에 저장되는 데이터 크기가 작지만 enum이 중간에 추가되면 데이터베이스에 기존에 저장되어있던 값은 따로 수정을 해주어야 한다..!!
value: TemporalType.DATE (2013-10-11)
TemporalType.TIME (11:11:11)
TemporalType.TIMESTAMP (2013-10-11 11:11:11)
datetime : MySQL
timestamp : Oracle, H2, PostgreSQL
지정할 수 있는 속성이 없다.
매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB으로 매핑
이 필 드는 매핑하지 않는다. 따라서 데이터베이스에 저장하지 않고 조회도 하지 않는다.
객체에 임시로 어떤 값을 보관하고 싶을 때 사용
JPA가 엔티티 데이터에 접근하는 방식 지정
AccessType.FIELD
, private 이어도 접근 가능AccessType.PROPERTY
접근자(Getter) 를 사용한다.