4. 엔티티 매핑
- 생성 일시
- 2023년 4월 11일 PM 10:43
1. @Entity#
- JPA를 사용해서 테이블과 매핑할 클래스는
@Entity어노테이션을 필수로 붙여야 한다. @Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라 부른다.

1.2. @Entity 적용시 주의사항#
- 기본 생성자는 필수다. (
NoArgsConstructor) fianl, enum, interface, inner클래스에는 사용할 수 없다.- 저장할 필드에 final을 사용하면 안 된다.
2. @Table#
@Table은 엔티티와 매핑할 테이블을 지정한다.- 생략시 엔티티 이름을 테이블 이름으로 사용한다.

3. 데이터베이스 스키마 자동 생성#
- JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다.
- 클래스의 매핑정보를 보명 너떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.
- JPA는 이 정보와 데이터베이스 방언을 사용해 스키마를 생성한다.
xml
<property name="hibernate.hbm2ddl.auto" value="create" />- 자바는 일반적으로 카멜 표기법을 사용하고 데이터베이스는 언더스코어 표기법을 사용하는데 데이터베이스 네이밍 전략 또한 변경할 수 있다.
xml
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />4. DDL 생성 기능#
- 회원 이름이 필수로 입력되어야 하고, 10자를 초과하면 안 된다는 제약조건이 추가된다면 아래와 같이
@Column의 옵션을 수정해야 한다.
java
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME", nullable = false, length = 10) //추가
private String username;
...
}- 테이블에 유니크 제약조건을 걸어야 한다면
@Table의uniqueConstraints속성을 사용하면 된다.
java
@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")
private String username;
private String age;
...
}5. 기본 키 매핑#
- JPA가 제공하는 데이터베이스 기본 키 생성 전략
- 직접 할당 = 기본 키를 애플리케이션에서 직접 할당한다.
- 자동 생성 = 대리 키 사용 방식
- IDENTITY = 키본 키 생성을 데이터베이스에 위임
- SEQUENCE = 데이터베이스 시퀀스를 사용해 기본 키를 할당
- TABLE = 키 생성 테이블을 사용
- 자동 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
- SEQUENCE나 IDENTITY는 데이터베이스에 의존한다.
- 키 생성 전략을 사용하려면 속성 추가가 반드시 필요하다.
- 하이버네이트는 하위호환을 위해 기본 값을
false로 두었다. - 이 옵션을 키면 키 생성 성능을 최적화하는
allocationSize속성을 사용하는 방식이 달라진다.
xml
<property name="hibernate.id.new_generator_mappings" value="true" />5.1. 기본 키 직접 할당 전략#
- 기본 키를 직접 할당하려면 다음 코드와 같이
@Id로 매핑하면 된다.
java
@Id
@Column(name = "id")
private String id;@Id적용 가능 자바 타입은 다음과 같다.- 자바 기본형
- 자바 래퍼(Wrapper)형
Stringjava.util.Datejava.sql.Datejava.math.BigDecimaljava.math.BigInteger
- 기본 키 직접 할당 전략은
em.persist()로 엔티티를 저장하기 전 애플리케이션에서 직접 할당해야 한다.- 기본 키 직접 할당 전략에서 식별자 값 없이 저장하면 예외가 발생하는데 어떤 예외가 발생하는지 JPA 표준에는 정의되어 있지 않다.
- 구현체로 사용하면
javax.persistence.PersistenceExption이 발생
java
Board board = new Board();
board.setId("id1"); //기본 키 직접 할당
em.persist(board);5.2. IDENTITY 전략#
- IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략이다.
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
sql
CREATE TABLE BOARD {
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
DATA VARCHAR(255)
};
INSERT INTO BOARD(DATA) VALUES('A');
INSERT INTO BOARD(DATA) VALUES('B');- 테이블을 생성할 때 기본 키 컬럼인 ID에
AUTO_INCREMENT를 추가함 - 개발자가 엔티티에 직접 식별자를 할당하면
@Id어노테이션만 있으면 된다.- 하지만 지금처럼 식별자가 생성되는 경우
@GeneratedValue어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다.
java
@Entity
public class Board {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
...
}5.3. SEQUENCE 전략#
- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다.
- SEQUENCE 전략은 이 시퀀스를 사용해 키본키를 생성한다.
- 주로 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
sql
CREATE TABLE BOARD (
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHAR(255)
)
//시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;java
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ",
initialValue = 1, allocationSize = 1)
public class Board {
@Id
@GeneraedValue(strategy = GenerationType.SEQUNCE, generator = "BOARD_SEQ_GENERATOR")
private Long id;
...
}@SequenceGenerator를 사용해BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록했다.@Id이제부터 식별자 값은BOARD_SEQ_GENERATOR시퀀스 생성기가 할당한다.
java
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}- IDENTITY 전략과 같지만 내부 동작 방식은 다르다.
- SEQUENCE 전략은
em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해 식별자를 조회한다. - 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.
- 이후 트랜잭션을 커밋해 플러시가 일어나며 데이터베이스에 저장한다.

5.3.1. 주의점#
- SequenceGenerator.allocationSize 기본값이 50이다.
- 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다.
5.4. TABLE 전략#
- TABLE 전략은 키 생성 저용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략
sql
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)java
@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;
}java
private static void logic(EntityManger em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}- TABLE 전략은 시퀀스 대신 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식이 같다.
- 키를 생성할 때마다
next_val컬럼 값이 증가한다. MY_SEQUENCES테이블에 값이 없으면 JPA가 값을 INSERT하면서 초기화하므로 값을 미리 넣어둘 필요는 없다.

5.4.1. TABLE 전략과 최적화#
- TABLE 전략은 값을 조회하며 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다.
- SEQUENCE전략과는 다르게 통신 수가 한번 더 늘어난다.
@TableGenerator.allocationSize를 사용하여 최적화 할 수 있다.
5.5. AUTO 전략#
- 데이터베이스의 종류도 많고 기본 키를 만드는 방법도 다양하다.
- AUTO 전략은 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
@GeneratedValue.strategy의 기본 값은 AUTO다.
- AUTO를 사용할 때 SEQUENCE나 TABLE전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다.
- 스키마 자동 생성을 사용하면 자동으로 만들어준다.
5.6. 기본 키 매핑 정리#
- 영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다.
5.6.1. 권장하는 식별자 선택 전략#
- 데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 함
- null 값은 허용하지 않는다.
- 유일해야한다.
- 변해선 안 된다.
- 테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.
- 자연 키 = 비즈니스에 의미가 있는 키
- 주민등록번호, 이메일, 전화번호
- 대리 키 = 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불림
- 오라클 시퀀스, 키생성 테이블 사용
- 자연키보다는 대리키를 권장한다.
- 현실과 비즈니스 규칙은 생각보다 쉽게 변하기 때문이다.
- 즉 통제할 수 없는것에 의지하지 말아야한다.
- JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.
- 테이블은 한번 정의하면 바꾸기가 어렵다.
- 외부 풍파에 쉽게 흔들리지 않는 대리 키가 일반적으로 좋은 선택이다.
6. 필드와 컬럼 매핑: 레퍼런스#

6.1. @Column#
@Column은 객체 필드를 테이블 컬럼에 매핑한다.- 가장 많이 사용되고 기능도 많다.
- 생략시 속성의 기본 값이 적용되며 자바의 기본타입을 이용할 경우
nullable값이false가 된다.
6.2. @Enumerated#
- Enum 타입을 매핑할 때 사용한다.
| 속성 | 기능 | 기본 값 |
| value | - EnumType.ORDINAL: enum 순서를 데이터베이스에 저장 - EnumType.STRING: enum 이름을 데이터베이스에 저장 | EnumType.ORDINAL |
- ORDINAL의 장단점
- 데이터베이스에 저장되는 데이터 크기가 작다.
- 이미 저장된
enum의 순서를 변경할 수 없다.
- STRING의 장단점
- 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다.
- 데이터베이스 저장되는 크기가 크다.
- 되도록 STRING을 사용하도록 하자.
6.3. @Temporal#
- 날짜 타입을 매핑할 때 사용한다.
6.4. @Lob#
- 데이터베이스 BLOB, CLOB 타입과 매핑한다.
6.4.1. 속성 정리#
@Lob은 지정 가능한 속성이 없지만 매핑하는 필드타입에 따라 자동 매핑된다.- CLOB :
String,char[],java.sql.CLOB - BLOB :
byte[],java.sql.BLOB
6.4.2. 사용 예#
java
// 매핑 코드
@Lob
private String lobString;
@Lob
private byte[] lobByte;
/*
// 오라클
lobString clob,
lobByte blob,
// MySQL
lobString longtext,
lobByte longblob,
// PostgreSQL
lobString text,
lobByte oid,
*/6.5. @Transient#
- 이 필드는 매핑하지 않는다.
- 조회도 저장도 하지 않기 때문에 객체에 임시로 어떤 값을 보관하고 싶을 때 쓴다.
6.6. @Access#
- JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
- 필드 접근
AccessType.FIELD로 지정한다.- 필드에 직접 접근하며
private제한자여도 접근 가능하다.
- 프로퍼티 접근
AccessType.PROPERTY로 지정한다.- 접근자(Getter)를 사용한다.