4장. 엔티티 매핑

배유연·2023년 10월 17일

1. @Entity

@Entity 는 JPA를 사용해서 테이블과 매핑할 클래스를 지정하는 것이다.

name 속성

JPA에서 사용할 엔티티 이름을 지정한다. name속성은 생략가능하다.
(설정하지 않으면 클래스 이름을 그대로 사용)

@Entity 사용시 주의사항

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

2. @Table

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

속성

속성명기능기본값
name매핑할 테이블 이름엔티티 이름을 사용한다.
catalogcatalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.-
schemaschema 기능이 있는 데이터베이스에서 schema를 매핑한다.-
uniqueConstraints(DDL)DDL 생성시에 유니크 제약조건을 만든다.-

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

persistence.xml

<property name="hibernate.hbm2ddl.auto" value="create"/>
  • 이렇게 해두면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 삭제 후 생성한다.
  • 자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다.
  • 운영환경에서 사용하지 않고 개발환경에서 사용하는 것이 좋다.

hibernate.hbm2ddl.auto 속성

옵션설명
create기존 테이블을 삭제하고 새로 생성한다. (DROP+CREATE)
create-drop(DROP+CREATE+DROP)
update데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
validate테이블과 엔티티를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.(DDL을 실행하지 않음)
none자동생성기능 사용하지 않음

이름 매핑 전략 변경하기

자바언어는 카멜표기법을 사용하고, 데이터베이스는 언더스코어를 주로 사용한다.

@Column(name="role_type") // 언더스코어로 구분
String roleType; // 카멜 표기법으로 구분

컬럼에 일일이 name을 언더스코어를 사용하긴 귀찮으므로,

<property name="hibernate.ejb.naming_strategy"
        value="org.hibernate.cfg.ImprovedNamingStrategy" />

를 설정해두면 된다.

4. DDL 생성기능

코드를 먼저 보자.

@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;
  • nullable = false
    not null제약조건을 추가할 수 있다.
  • length
    문자의 크기를 지정할 수 있다.
  • uniqueConstraints
    유니크 제약조건이 추가된다.

위 조건들은 모두 DDL을 자동으로 생성할 때만 사용되고 JPA실행 로직에는 영향을 주지 않는다.

5. 기본 키 매핑

JPA가 제공하는 데이터베이스 기본키 생성 전략

  • 직접할당 : 기본 키를 애플리케이션에서 직접할당하는 방식
  • 자동생성 : 대리키 사용 방식
    -IDENTITY : 기본키 생성을 데이터베이스에 위임한다.
    -SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본키를 할당한다.
    -TABLE : 키 생성 테이블을 사용한다.

키 생성전략을 사용하려면 persisence.xml 에 hibernate.id.new_generator_mappings를 true 로 설정해줘야한다.

🔑기본 키 직접 할당 전략

기본키를 직접 할당하려면 @Id로 매핑하면 된다.

  @Id
  @Column(name = "id")
  private String id;

🔑IDENTITY 전략

IDENTITY 는 기본키 생성을 데이터베이스에 위임하는 전략이다. DB의 IDENTITY컬럼을 이용하는 방식으로 데이터베이스에 의존적이다.

주로 MySQL, PostgreSQL, SQL Server, DB2 에서 사용한다.
(예. MySQL 의 AUTO_INCREMENT 기능)

@Entity
public class Board{
  @Id
  @GenerateValue(strategy = GenerationType.IDENTITY)
  private Long id;
  ...
}

위 처럼 @GenerateValue어노테이션에 IDENTITY 전략을 사용한다.

여기서 해당 ID값을 읽어왔을 때,

 private static void logic2(EntityManager em) {
	Board board = new Board();
    em.persist(board);
    System.out.println("board.id = " + board.getId());
}

출력된 값은 데이터베이스가 생성한 값을 JPA가 조회한 것이다.
IDENTITY 전략은 데이터를 데이터베이스에 INSERT 한 후에 기본 키 값을 조회할 수 있다.
따라서 엔티티에 식별자 값을 할당하려면 JPA는 추가로 데이터베이스를 조회해야 한다.

INDENTITY 식별자 생성 전략은 쓰기지연이 안된다!
해당 전략은 엔티티를 저장해야 식별자를 구할 수 있으므로 em.persist()호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.

🔑SEQUENCE 전략

시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다.
이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.

SequenceGenerator 속성

속성명기능기본값
name식별자 생성기 이름필수
sequenceName데이터베이스에 등록되어 있는 시퀀스 이름hibernate_sequene
initialValue시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정1
allocationSize시퀀스 한 번 호출에 증가하는 수50
name식별자 생성기 이름필수
@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")
    //@SequenceGenerator를 여기에 추가해도 된다.
    private Long id;

 ...
}
  • SequenceGenerator 로 시퀀스 생성기 등록
  • SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.
    (이전에 봤던 IDENTITY전략은 먼저 엔티티를 DB에 저장한 후 식별자를 조회해서 엔티티의 식별자에 할당한다.)

SEQUENCE 전략과 최적화

SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업후 INSERT를 진행한다. 따라서 데이터베이스와 2번 통신한다.
그렇다면 접근을 최소화하기 위한 방법은 무엇일까??

@SequenceGenerator.allocationSize
해당 속성을 통해 최적화를 한다. 설정한 값 만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스값을 할당한다.

기본값이 50이므로, 한번 50번 증가시키고, 메모리에서 식별자를 할당한다.
여러 JVM이 동시에 접근해도 기본키 값이 충돌하지 않는 장점이 있다.

만약, 시퀀스값이 한번에 많이 올라가는게 부담스럽거나, INSERT 성능이 중요하지 않다면 1로 설정하고 사용하면 된다.

🔑TABLE 전략

테이블을 이용하여 데이터베이스 시퀀스를 흉내내는 전략이다. 모든 데이터베이스에서 사용할 수 있다.

@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") // table 전략
    private Long id;
...
}

코드를 보면 사용방법을 알 수 있다.
SEQUENCE 전략과 내부 동작방식이 같다.

TABLE 전략 최적화
TABLE 전략은 값을 조회하면서 SELECT 쿼리한번과 값을 증가시키는 UPDATE쿼리를 한번 더 사용한다. 즉, SEQUENCE 전략과비교해서 DB와 한 번 더 통신한다.
얘도 최적화 하기 위해서 @TableGenerator.allocationSize 를 사용하면 된다.

🔑AUTO 전략

선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중하나를 자동으로 선택한다.

  • GeneratedValue.strategy 의 기본값은 AUTO이다.
  • AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다.

em.persist 호출시 키 생성 전략별 정리

  • 직접할당 : em.persist() 호출하기 전에 직접 식별자 값을 할당해야한다.
  • SEQUENCE : 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장
  • TABLE : SEQUENCE 와 동일, 테이블에서 가져오는 것만 빼고!
  • IDENTITY : 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.

권장하는 식별자 선택 전략

자연키 : 비즈니스에 의미가 있는 키(주민등록번호, 전화번호..)
대리키 : 비즈니스와 관련 없는 임의의 키, 대체키

자연 키보다는 대리키를 권장한다. 왜냐하면 비즈니스 환경은 언젠가 변할 수 있기 때문이다.

6. 필드와 컬럼 매핑

@Column

@Column 은 객체 필드를 테이블 컬럼에 매핑한다.

속성
name : 필드와 매핑할 테이블의 컬럼 이름
nullable : null값의 허용여부를 설정, 자바 기본타입 사용시 nullable = false가 안전하다.
unique : 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
columnDefinition : 데이터베이스 컬럼 정보를 직접 줄 수 있다.
length : 문자 길이 제약조건
percision, scale : BigDecimal 타입에서 사용, precision은 소수점을 포함한 전체자리, scale은 소수의 자릿수

@Enumerated

자바의 enum타입을 매핑할 때 사용한다.

@Enumerated(EnumType.STRING)
enum 이름을 그대로 세팅한다.(권장 설정)

@Enumerated(EnumType.ORDINAL)
enum순서를 데이터베이스에 저장한다.(Default)

@Temporal

날짜타입을 매핑할 때 사용한다. @Temporal 생략시 timestamp 로 설정된다.

@Temporal(TemporalType.DATE)
데이터베이스 date타입과 매핑(ex 2013-10-17)

@Temporal(TemporalType.TIME)
데이터베이스 time타입과 매핑(ex 22:10:00)

@Temporal(TemporalType.TIMESTAMP)
데이터베이스 timestamp와 매핑(ex 2013-10-17 22:10:00)

@Lob

데이터베이스 BLOB, CLOB 타입과 매핑한다.(외부 바이너리 파일, 큰데이터를 외부파일로 저장하는 용도)

@Trasient

이 필드는 매핑하지 않는다.

@Access

JPA가 엔티티 데이터에 접근하는 방식을 지정한다.

  • @Access(AccessType.FIELD) : 필드에 설정한다.
  • @Access(AccessType.PROPERTY) : getter에 설정한다.
profile
기초를 중요시하는 백엔드개발자입니당

0개의 댓글