////
Search

4. 엔티티 매핑

태그
생성 일시
2023/04/11 13:43

1. @Entity

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다.
@Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라 부른다.

1.2. @Entity 적용시 주의사항

기본 생성자는 필수다. (NoArgsConstructor)
fianl, enum, interface, inner 클래스에는 사용할 수 없다.
저장할 필드에 final을 사용하면 안 된다.

2. @Table

@Table은 엔티티와 매핑할 테이블을 지정한다.
생략시 엔티티 이름을 테이블 이름으로 사용한다.

3. 데이터베이스 스키마 자동 생성

JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다.
클래스의 매핑정보를 보명 너떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.
JPA는 이 정보와 데이터베이스 방언을 사용해 스키마를 생성한다.
<property name="hibernate.hbm2ddl.auto" value="create" />
XML
복사
스키마 생성 허용 옵션
자바는 일반적으로 카멜 표기법을 사용하고 데이터베이스는 언더스코어 표기법을 사용하는데 데이터베이스 네이밍 전략 또한 변경할 수 있다.
<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
XML
복사

4. DDL 생성 기능

회원 이름이 필수로 입력되어야 하고, 10자를 초과하면 안 된다는 제약조건이 추가된다면 아래와 같이 @Column의 옵션을 수정해야 한다.
@Entity @Table(name="MEMBER") public class Member { @Id @Column(name = "ID") private String id; @Column(name = "NAME", nullable = false, length = 10) //추가 private String username; ... }
Java
복사
테이블에 유니크 제약조건을 걸어야 한다면 @TableuniqueConstraints 속성을 사용하면 된다.
@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; ... }
Java
복사

5. 기본 키 매핑

JPA가 제공하는 데이터베이스 기본 키 생성 전략
직접 할당 = 기본 키를 애플리케이션에서 직접 할당한다.
자동 생성 = 대리 키 사용 방식
IDENTITY = 키본 키 생성을 데이터베이스에 위임
SEQUENCE = 데이터베이스 시퀀스를 사용해 기본 키를 할당
TABLE = 키 생성 테이블을 사용
자동 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
SEQUENCE나 IDENTITY는 데이터베이스에 의존한다.
키 생성 전략을 사용하려면 속성 추가가 반드시 필요하다.
하이버네이트는 하위호환을 위해 기본 값을 false로 두었다.
이 옵션을 키면 키 생성 성능을 최적화하는 allocationSize 속성을 사용하는 방식이 달라진다.
<property name="hibernate.id.new_generator_mappings" value="true" />
XML
복사

5.1. 기본 키 직접 할당 전략

기본 키를 직접 할당하려면 다음 코드와 같이 @Id로 매핑하면 된다.
@Id @Column(name = "id") private String id;
Java
복사
@Id적용 가능 자바 타입은 다음과 같다.
자바 기본형
자바 래퍼(Wrapper)형
String
java.util.Date
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전 애플리케이션에서 직접 할당해야 한다.
기본 키 직접 할당 전략에서 식별자 값 없이 저장하면 예외가 발생하는데 어떤 예외가 발생하는지 JPA 표준에는 정의되어 있지 않다.
구현체로 사용하면 javax.persistence.PersistenceExption이 발생
Board board = new Board(); board.setId("id1"); //기본 키 직접 할당 em.persist(board);
Java
복사

5.2. IDENTITY 전략

IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략이다.
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
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');
SQL
복사
테이블을 생성할 때 기본 키 컬럼인 ID에 AUTO_INCREMENT를 추가함
개발자가 엔티티에 직접 식별자를 할당하면 @Id 어노테이션만 있으면 된다.
하지만 지금처럼 식별자가 생성되는 경우 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다.
@Entity public class Board { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int id; ... }
Java
복사

5.3. SEQUENCE 전략

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다.
SEQUENCE 전략은 이 시퀀스를 사용해 키본키를 생성한다.
주로 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
CREATE TABLE BOARD ( ID BIGINT NOT NULL PRIMARY KEY, DATA VARCHAR(255) ) //시퀀스 생성 CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
SQL
복사
시퀀스 DDL
@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; ... }
Java
복사
시퀀스 매핑 코드
@SequenceGenerator를 사용해 BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록했다.
@Id이제부터 식별자 값은 BOARD_SEQ_GENERATOR시퀀스 생성기가 할당한다.
private static void logic(EntityManager em) { Board board = new Board(); em.persist(board); System.out.println("board.id = " + board.getId()); }
Java
복사
시퀀스 사용 코드
IDENTITY 전략과 같지만 내부 동작 방식은 다르다.
SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해 식별자를 조회한다.
그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.
이후 트랜잭션을 커밋해 플러시가 일어나며 데이터베이스에 저장한다.

5.3.1. 주의점

SequenceGenerator.allocationSize 기본값이 50이다.
데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다.

5.4. TABLE 전략

TABLE 전략은 키 생성 저용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략
create table MY_SEQUENCES ( sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name) )
SQL
복사
TABLE 전략 생성 DDL
@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
복사
TABLE 전략 매핑 코드
private static void logic(EntityManger em) { Board board = new Board(); em.persist(board); System.out.println("board.id = " + board.getId()); }
Java
복사
TABLE 전략 매핑 사용 코드
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. 사용 예

// 매핑 코드 @Lob private String lobString; @Lob private byte[] lobByte; /* // 오라클 lobString clob, lobByte blob, // MySQL lobString longtext, lobByte longblob, // PostgreSQL lobString text, lobByte oid, */
Java
복사

6.5. @Transient

이 필드는 매핑하지 않는다.
조회도 저장도 하지 않기 때문에 객체에 임시로 어떤 값을 보관하고 싶을 때 쓴다.

6.6. @Access

JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
필드 접근
AccessType.FIELD로 지정한다.
필드에 직접 접근하며 private 제한자여도 접근 가능하다.
프로퍼티 접근
AccessType.PROPERTY로 지정한다.
접근자(Getter)를 사용한다.