자바 ORM 표준 JPA 프로그래밍 학습 내용 정리한 포스팅 입니다.
객체와 테이블 매핑 : @Entity
, @Table
기본 키 매핑 : @Id
필드와 컬럼 매핑 : @Column
연관관계 매핑 : @ManyToOne
, @JoinColumn
테이블을 매핑할때 @Entity
어노테이션을 필수로 붙어야 한다.
속성
기능
기본값
name
JPA 에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다.
설정하지 않으면 클래스 이름을 그대로 사용.
매개변수가 없는 기본 생성자는 필수.
final
, enum
, interface
, inner
클래스에는 사용할 수 없다.
저장할 필드에 final
을 사용하면 안된다.
@Table
은 엔티티와 매핑할 테이블을 지정한다.
속성
기능
기본값
name
매핑할 테이블 이름
엔티티 이름을 사용.
catalog
catalog 기능이 있는 데이터베이스에서 catalog 를 매핑
schema
shcema 기능이 있는 데이터베이스에서 schema 를 매핑
uniqueConstrains(DDL)
DDL 생성 시 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 이 기능은 스키마 자동생성 기능을 사용해서 DDL 를 만들때만 사용
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package jpabook.start;
import javax.persistence.*;
import java.util.Date;
@Entity
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME", nullable = false, length = 10)
private String username;
private Integer age;
@Enumerated(EnumType.STRING) // 자바의 Enum 클래스 이용
private RoleType roleType;
@Temporal(TemporalType.DATE) // 자바의 날짜타입 매핑
private Date createDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob // BLOB, CLOB 타입 매핑
private String description;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public RoleType getRoleType() {
return roleType;
}
public void setRoleType(RoleType roleType) {
this.roleType = roleType;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Date lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
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" />
@Column
매핑정보에 nullable 속성 값을 false 로 지정하여 NOT NULL 제약 조건 추가.
length 를 지정하면 문자의 크기를 지정할 수 있다.
1
2
3
// Member.java 추가
@Column ( name = "NAME" , nullable = false , length = 10 )
private String username ;
DDL 자동생성 기능
@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 {...}
DDL 을 자동생성할 때만 사용하고 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 전략은 기본 키 생성을 데이터베이스에 위임하는 전략이다.
주로 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 전략의 경우 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
데이터베이스의 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터 베이스 오브젝트이다.
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 ;
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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;
}
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 전략은 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 만들어 데이터 시퀀스처럼 사용하는 전략이다.
이 전략은 모든 데이터베이스에 적용이 가능하다.
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 )
)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity
@TableGenerator (
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnVlaue = "BOARD_SEQ", allocationSize = 1)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
@TableGenerator
속성
기능
기본값
name
식별자 생성기 이름
필수
table
키생성 테이블명
hibernate_sequence
pkColumnName
시퀀스 컬럼명
sequence_name
valueColumnName
시퀀스 값 컬럼명
next_val
initialValue
초기 값, 마지막으로 생성된 값이 기준이다
0
allocationSize
시퀀스 한 번 호출에 증가하는 수 (성능최적화 이용)
50
catalog , schema
데이터베이스 catalog, schema 이름
uniqueConstrains(DDL)
유니크 제약 조건을 지정할 수 있다.
GenerationType.AUTO
는 선택한 데이터베이스 방언에 따라 IDENTITY
, SEQUENCE
, TABLE
전략 중 하나를 자동으로 선택.
AUTO 는 데이터베이스를 변경해도 코드를 수정할 필요가 없는 장점이 있다.
영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 반드시 식별자 값이 있어야 한다.
null 값은 허용하지 않는다.
유일해야 한다.
변해서는 안된다.
자연키 (natural key) : 비즈니스에 의미가 있는 키. ex) 주민등록번호, 이메일, 전화번호
대리키 (surrogate key) : 비즈니스와 관련없는 임의로 만들어진 키. ex) 오라클 시퀀스, auto_increment, 키생성 테이블
자연키보다는 대리 키를 권장한다.
@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
자바의 enum
타입을 매핑할 때 사용한다.
EnumType.ORDINAL
: enum 에 정의된 순서대로 데이터베이스에 저장된다.
장점 : 저장되는 데이터 크기가 작다.
단점 : 이미 저장된 enum 순서를 변경할 수 없다.
EnumType.STRING
: enum 이름을 데이터베이스에 저장. (권장)
장점 : 저장된 enum 순서가 바뀌거나 enum이 추가되어도 안전
단점 : 데이터베이스에 저장되는 크기가 ORINAL 에 비해 크다.
날짜 타입 (java.util.Date
, java.util.Calendar
) 을 매핑할 때 사용.
TemporalType.DATE
, TemporalType.TIME
, TemporalType.TIMESTAMP
데이터베이스의 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
데이터베이스에 저장하지도 조회하지도 않는다. 객체에 어떤 값을 임시로 보관하고 싶을 때 사용한다.
JPA 가 엔티티 데이터에 접근하는 방식을 지정
필드접근 : AccessType.FIELD
로 지정한다. 필드에 직접 접근하며, 필드 접근 권한이 private 이어도 접근 가능하다.
프로퍼티 접근 : AccessType.PROPERTY
로 지정. 접근자를 사용한다.