김영한님의 인프런 강의 '자바 ORM 표준 JPA 프로그래밍'을 참고했습니다.
@Entity
가 붙은 클래스는 JPA가 관리한다. 따라서 JPA를 사용해서 테이블과 매핑할 클래스에는 @Entity
를 꼭 붙여야 한다.
기본 생성자가 필수이다.
case 1 : 생성자를 생략할 경우 -> 자바는 생성자가 하나도 없으면 기본 생성자를 자동으로 만들어준다.
case 2 : 생성자가 1개라도 있으면 -> 기본 생성자를 직접 만들어야 한다.
속성 | 기능 | 기본값 |
---|---|---|
name | JPA에서 사용할 엔티티 이름을 지정한다. | 클래스 이름을 그대로 사용한다. |
다른 패키지에 같은 클래스 이름이 없으면 가급적 기본값을 사용한다.
@Table
은 엔티티와 매핑할 테이블을 지정한다.속성 | 기능 | 기본값 |
---|---|---|
name | 매핑할 테이블 이름. | 엔티티 이름을 사용한다. |
catalog | catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다. | |
schema | schema 기능이 있는 데이터베이스에서 schema를 매핑한다. | |
uniqueConstraints(DDL) | DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다. |
지금까지는 테이블을 먼저 생성하고 그 다음에 엔티티를 만들었지만 데이터베이스 스키마 자동 생성을 사용하면 엔티티만 만들면 테이블을 자동으로 생성할 수 있다.
예를 들어 다음과 같이 엔티티를 만들었을 뿐인데
그에 맞는 DDL이 자동으로 만들어짐을 확인할 수 있다.
(hibernate.show_sql 속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다)
설정만 하면 JPA는 데이터베이스 방언을 참고해서 데이터베이스에 맞는 적절한 DDL을 생성해 준다.
가변 문자의 경우 oracle은 VARCHAR2, mysql은 VARCHAR이다. persistence.xml에 설정한 방언에 따라 적절한 DDL을 생성해 준다.
데이터베이스 스키마 자동 생성 기능을 사용하려면 persistence.xml에 다음 속성을 추가하면 된다.
<property name="hibernate.hbm2ddl.auto" value="create" />
이 속성을 추가하면 애플리케이션 실행 시점에 기존 테이블을 삭제하고 테이블을 새로 만들어준다. 아래에 속성들을 정리해 봤다.
옵션 | 설명 |
---|---|
create | 기존 테이블을 삭제하고 새로 생성한다. |
create-drop | create와 같으나 종료 시점에 테이블을 drop 한다. |
update | 변경 사항만 수정한다. |
validate | 엔티티와 테이블이 정상 매핑되었는지만 확인한다. |
none | 자동 새성 기능을 사용하지 않는다. |
개발 환경에 따른 전략
운영 서버에서 create, create-drop, update처럼 DLL을 수정하는 옵션은 절대로 사용하면 안된다. 복구하기 힘든 심각한 에러가 발생한다.
스키마 자동 생성하기를 통해 만들어지는 DDL에 다양한 제약 조건을 추가할 수 있다. 이러한 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다. 그러면 만약 DDL을 직접 만든다면 사용할 이유가 없을까?
답은 아니다. 이 기능을 사용하면 애플리케이션 개발자가 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다.
@Column
은 객체 필드를 테이블 컬럼에 매핑한다. 가장 많이 사용되고 기능도 많다.
속성 | 설명 | 기본값 |
---|---|---|
name | 필드와 매핑할 테이블의 컬럼 이름 | 객체의 필드 이름 |
insertable, updatable | 등록, 변경 가능 여부 | TRUE |
nullable(DDL) | null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다. | TRUE |
unique(DDL) | @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. | |
columnDefinition(DDL) | 데이터베이스 컬럼 정보를 직접 줄 수 있다. ex) varchar(100) default 'EMPTY' | 필드의 자바 타입과 방언 정보를 사용 |
length(DDL) | 문자 길이 제약조건, String 타입에만 사용한다. | 255 |
precision, scale(DDL) | BigDecimal 타입에서 사용한다(BigInteger도 사용할 수 있다). precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다. | precision=19, scale=2 |
속성 중에 주로 name, nullable이 사용되고 나머지는 잘 사용되지 않는다.
자바 enum 타입을 매핑할 때 사용한다.
속성 | 설명 | 기본값 |
---|---|---|
value | • EnumType.ORDINAL: enum 순서를 데이터베이스에 저장 • EnumType.STRING: enum 이름을 데이터베이스에 저장 EnumType.ORDINAL | EnumType.ORDINAL |
ORDINAL은 사용하지 말자. 위험하다!!
날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용한다.
속성 | 설명 | 기본값 |
---|---|---|
value | • TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑 • TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑 • TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이 스 timestamp 타입과 매핑 |
@Temporal
은 요새는 잘 사용 안함. LocalDate, LocalDateTime을 대신 사용!!
데이터베이스 BLOB, CLOB 타입과 매핑
• @Lob
에는 지정할 수 있는 속성이 없다.
• 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑
• CLOB: String, char[], java.sql.CLOB
• BLOB: byte[], java.sql. BLOB
• 필드 매핑X
• 데이터베이스에 저장X, 조회X
• 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
ex)
@Transient
private Integer temp;
데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.
테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.
자연 키보다는 대리키를 사용하자!!
JPA가 제공하는 데이터베이스 기본 키 생성 전략은 다음과 같다.
기본 키를 직접 할당하려면 @ID
만 사용하면 된다.
@Entity
public class Member {
@Id
private String id;
@Column(name = "name")
private String username;
//getter setter
...
직접 할당은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당한다. 할당하지 않으면 예외가 발생한다.
.
.
Member member = new Member();
member.setId("member1"); // 기본 키를 직접 할당한다.
member.setUsername("a");
em.persist(member);
tx.commit; //트랜잭션 커밋
.
.
IDENTITY 전략은 기본 키 생성을 데이터베이스에 위임한다. 주로 MySQL에서 사용한다. IDENTITY 전략에서는 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다는 작은 단점이 있다.
IDENTITY 전략을 사용해 기본 키를 생성하고 DDL을 만들어보자.
먼저 persistence.xml에서 방언을 MySQL로 바꾸자.
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
@GeneratedValue
의 strategy 속성 값을 GenerationType.IDENTITY로 하자. auto_increment는 정수이므로 Long 타입을 사용해야 한다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String username;
//getter setter
...
}
이제 애플리케이션에서 기본 키를 할당안해도 된다.
.
.
Member member = new Member();
member.setUsername("a");
em.persist(member);
tx.commit; //트랜잭션 커밋
.
.
생성된 DDL
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다. 주로 오라클, H2 데이터베이스에서 사용한다. 사용법은 앞서 IDENTITY 전략과 마찬가지로 방언을 H2나 오라클로 바꾸고 strategy 속성 값을 GenerationType.SEQUENCE로 바꾸면 된다.
아래와 같이 데이터베이스에 시퀀스가 생성되었고 기본키 1이 저장되었음을 확인할 수 있다.
위에서는 따로 시퀀스 이름을 지정해 주지 않아서 기본 시퀀스인 hibernate_sequence가 사용되었다. 테이블마다 시퀀스를 따로 관리하고 싶거나 속성을 추가하려면@SequenceGenerator
를 사용해 매핑하면 된다.
@SequenceGenerator
속성
속성 | 설명 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 |
IDENTITY 전략과 달리 JPA가 pk 값을 내부적으로 데이터베이스에서 영속성 컨텍스트로 가져올 수 있다.
JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 allocationSize를 사용한다. 여기에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다. 예를 들어 allocationSize 값이 50이면 시퀀스를 한 번에 50 증가시킨 다음 1~50까지는 메모리에서 식별자를 할당한다. 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51~100까지 메모리에서 식별자를 할당한다. 이 최적화 방법은 동시성 이슈와 상관없다는 장점이 있지만 시퀀스 값이 한번에 많이 증가하므로 적절한 allocationSize 값을 설정하는것이 중요하다.
TABLE 전략은 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내 내는 전략이다. 모든 데이터베이스에 적용 가능하다는 장점이 있지만 성능이 조금 떨어진다는 단점이 있다. 앞서 SEQUENCE 전략과 마찬가지로 방언과 strategy 속성값을 바꾸고@TableGenerator
를 쓰면 된다. TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식이 같다.
@TableGenerator
속성
속성 | 설명 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnName | 시퀀스 값 컬럼명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값, 마지막으로 생성된 값이 기준이다. | 0 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
uniqueConstraints(DDL) | 유니크 제약 조건을 지정할 수 있다. |
AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다. 예를 들어 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용한다.