본문 바로가기
Spring/Spring JPA

[Spring JPA] JPA 엔티티 매핑(Entity Mapping)

by dbjh 2021. 3. 24.
반응형

이번 글에서는 JPA를 사용하여 DB Table과 Entity매핑에 대해서 알아보도록하자. Entity는 그냥 간단히 JPA가 관리하는 Class 정도로 인식하면된다. Class의 필드와 Table의 컬럼을 매핑하고 객체와 테이블을 매핑하는 과정에 대해서 알아보도록 하자.

출처 : 구글검색

JPA에서는 대표적으로 아래의 어노테이션을 사용한다.

객체와 테이블 매핑: @Entity , @Table
기본 키 매핑: @Id
필드와 컬럼 매핑: @Column
연관관계 매핑: @ManyToOne , @JoinColumn

 

우선 객체와 테이블을 매핑하는 @Entity 어노테이션을 알아보도록하자.

1. @Entity 

테이블과 매핑할 클래스에는  @Entity 어노테이션인 반드시 명시되어야한다.

@Entity 어노테이션에 적용할 수 있는 속성은 아래와 같다.

  • name :
    • JPA에서 사용할 엔티티 이름을 지정하고 기본값으로 클래스명을 사용함.
    • 만약 다른 패키지에 이름이 같은 이름이 있다면 직접 지정하여 충돌을 방지해야함
    • EX) @Entity(name = "테이블 명")

@Entity 어노테이션을 사용할때 주의점이 있는데, public이나 protected 접근지정자인 기본 생성자가 필수다. 그리고 final 클래스, enum , interface , inner 클래스에는 사용할 수 없으며, Table 컬럼에 매핑한 필드에 final 을 사용하면 안 된다.

 

2. @Table

엔티티와 매핑할 테이블을 지정하기 위해선, @Table 어노테이션을 사용해야한다. 만약 해당 어노테이션을 지정하지 않는다면, 엔티티 명을 테이블 명으로 사용한다.

@Table 어노테이션에 적용할 수 있는 속성은 아래와 같다.

  • name :
    • 매핑할 테이블 이름을 설정하며 기본 값으로 엔티티 이름을 사용
  • catalog :
    • 카탈로그 기능이 있는 데이터베이스에서 catalog를 매핑
  • shema :
    • schema 기능이 있는 데이터베이스에서 schema 를 매핑
  • uniqueConstraints(DDL)
    • DDL 생성 시에 유니크 제약조건을 만들며. 2개 이상의 복합 유니크 제약조건도 만들 수 있음
    • *스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용 가능

 

3. 기본 키(Primary Key) 매핑

JPA는 DB의 시퀀스로 관리되는 값을 기본 키로 어떻게 사용할까? MySQL, Oracle 등등 DB 마다 기본키를 생성하는 방식이 다른데, JPA는 다 수의 DB에 적용하여 사용해야 하기때문에 이 문제를 해결해야 했다.

JPA의 DB 기본 키 생성 방식은 아래와 같다.

  • 직접 할당 : 기본 키를 애플리케이션에서 직접 할당함
  • 자동 생성 : 대리 키 사용 방식
    1. IDENTITY : PK 생성을 데이터베이스에게 위임 - DB에 의존 O
    2. SEQUENCE : DB 시퀀스를 사용하여 기본 키 할당 - DB에 의존 O
    3. TABLE : 키생성 테이블을 만들어서 사용 - DB에 의존 X

* Oracle은 시퀀스를 제공하고, MySQL은 AUTO_INCREMENT 기능을 제공한다. DB마다 시퀀스 관리 전략이 다르기 때문에 IDENTITY, SEQUENCE 전략은 DB에 의존하게 되고, TABLE 전략은 모든 RDB는 테이블을 관리하기 때문에 모든 DB에서 사용이 가능하다.

기본키 할당시 @Id 를 사용하면 되고, 자동생성 전략을 사용하려면, @GeneratedValue 를 추가하면된다.

* 키 생성 전략을 사용하려면 설정을 hibernate.id.new_generator_mappings=true로 해줘야한다. 기본 값은 false로 설정되어 있지만, 키 생성 전략을 사용하기 위해 반드시 true로 설정해야한다.

지금부터는 각 기본 키 생성전략을 예로 들면서 알아보도록 하자.

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

위처럼 @Id를 사용하여 기본 키를 설정할 수 있다고 위에서도 언급했는데, Java에서  @Id를 적용할 수 있는 타입은 String, java.util.Date , java.sql.Date , java.math.BigDecimal , java.math.BigIntege 등이 있다.


직접할당 전략

첫번째로, 직접 할당은 아래와 같이 코드를 보면 직관적으로 알 수 있다.

// 사용방식

Board board = new Board(); 
board.setId("id1") //기본 키 직접 할당 
em.persist(board);

객체를 생성하고 해당객체에 기본 키의 value값을 세팅하는 것이다.


IDENTITY 전략

두번째로, IDENTITY는 PK(Primary Key) 생성을 DB에 위임하는 전략이고 MySQL, PostgreSQL, SQL Server, DB2에서 주로 사용한다.

아래의 DDL 쿼리를 보도록하자.   

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');

MySQL에서는 위처럼 AUTO_INCREMENT를 설정할 수 있는데, 이렇게 설정하면 데이터를 INSERT 할 때 ID컬럼을 비워두면 0, 1, 2 .... 996, 997순으로 자동으로 값이 1씩 증가하면서 저장된다.

IDENTITY 전략은 데이터(ROW)가 저장된 상태로 기본 키를 구할 수 있을때 사용한다. 그리고 개발자가 직접 식별자를 할당하면 @Id 어노테이션만 해당 필드에 선언하면 되지만 위처럼 자동으로 생성되는 경우에는@GeneratedValue 어노테이션을 추가로 명시하고 식별자 생성 전략을 선택해야 한다. IDENTITY 전략을 사용하려면 strategy 속성 값을 GenerationType.IDENTITY 로 지정하면 된다. 아래와 같이 선언하면 JPA는 기본 키 값을 얻어오기 위해 DB를 추가로 조회한다.

// 선언방식

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

 

// 사용 방식

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

위의 코드가 동작하면 Board 객체를 저장하고 저장된 데이터(ROW)의 새로 생성된 ID 컬럼을 가져와서 출력하게 된다.

* JDBC3에 추가된 Statement.getGeneratedKeys() 를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어 올 수 있다.


SEQUENCE 전략

세번째로, SEQUENCE는 유일한 값을 순서대로 생성하는 DB 기능인데, 시퀀스를 지원하는 Oracle, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.

아래의 DDL을 보면 위의 AUTO_INCREMENT와는 다르게 시퀀스를 테이블과 별개로 생성한다.

CREATE TABLE BOARD (
 ID BIGINT NOT NULL PRIMARY KEY,
 DATA VARCHAR(255)
)
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;

 

해당 시퀀스 및 테이블에 매핑하는 Entitiy는 아래와 같이 선언한다.

// 선언방식

@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;
   ...
}

 

@SequenceGenerator를 이용하여 BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록하고, sequenceName 속성을 이용하여 DB에서 생성한 "BOARD_SEQ"와 매핑시킨다.

// 사용 방식

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

SEQUENCE 전략은 em.persist() 실행시, DB 시퀀스를 사용해서 ID를 조회한다. 그리고 조회한 ID를 엔티티에 세팅한 후 DB에 저장한다. 

* IDENTITY 전략 : 선(Entity)저장 후(식별자)조회 /  SEQUENCE 전략 : 선(식별자)조회 후(Entity)저장 

@SequenceGenerator 어노테이션에 적용할 수 있는 속성은 아래와 같다.(기본 값은 하이버네이트 기준)

  • name :
    • 식별자 생성기 이름을 설정가능하며 기본 값이 필수
  • sequenceName :
    • 데이터베이스에 등록되어 있는 시퀀스 이름
    • 기본값 = hibernate_sequence
  • initialValue :
    • DDL 생성시에만 사용됨, 시퀀스 DDL을 생성 할 때 처음 시작하는 수를 지정함
    • 기본값 = 1
  • allocationSize :
    • 시퀀스 한 번 호출에 증가하는 수 (성능 최적화 에 사용됨)
    • 기본값 = 50
  • catalog, schema :
    • 데이터베이스 catalog,schema 이름

@SequenceGenerator 어노테이션에 매핑되는 DDL은 아래와 같다.

create sequence [sequenceName] 
start with [initialValue] increment by [allocationSize]

* JPA 표준 명세에는 sequenceNamedml 기본값을 JPA 구현체가 정의함.


TABLE 전략

네번째로, TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터 베이스 시퀀스를 흉내 내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.

테이블생성 DDL과 해당 테이블과 매핑하는 Entity 코드를 살펴보도록 하자.

# sequence_name 컬럼을 시퀀스 이름으로 사용하고 next_val 컬럼을 시퀀스 값으로 사용
create table MY_SEQUENCES (
 sequence_name varchar(255) not null ,
 next_val bigint,
 primary key ( sequence_name ) 
)
// 선언 방식

@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

@TableGenerator를 사용해서 테이블 키 생성기를 등록하며 "BOARD_SEQ_GENERATOR" 라는 이름의 테이블 키 생성기를 등록하고 방금 생성한 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑한다. 다음으로 TABLE 전략을 사용하기 위해 GenerationType.TABLE 을 선택했다. generator 속성을 이용해 "BOARD_SEQ_GENERATOR" 키 생성기를 지정했다.

// 사용 방식

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

TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 같다.

@TableGenerator 어노테이션에 적용할 수 있는 속성은 아래와 같다. (기본 값은 하이버네이트 기준)

  • name :
    • 식별자 생성기 이름을 설정가능하며 기본 값이 필수
  • table:
    • 키생성 테이블명
    • 기본값 = hibernate_sequence
  • pkColumnName :
    • 시퀀스 컬럼명
    • 기본값 = sequence_name
  • valueColumnName :
    • 시퀀스 값 컬럼명
    • 기본값 = next_val
  • pkColumnValue :
    • 키로 사용할 값 이름
    • 기본값 = Entity 명
  • initialValue :
    • 초기 값, 마지막으로 생성된 값이 기 준이다.
    • 기본값 = 0
  • allocationSize :
    • 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨)
    • 기본값 = 50
  • catalog, schema :
    • 데이터베이스 catalog,schema 명
  • uniqueConstraints(DDL) :
    • 유니크 제약 조건을 지정할 수 있다.

* TABLE 전략은 SELECT 쿼리로 값을 조회하고 UPDATE 쿼리로 값을 증가시킨다. SEQUENCE 전략과 비교했을때 DB와 한 번 더 통신하는 것이 단점이다. TABLE 전략 최적화는 @TableGenerator.allocationSize 를 사용하면 된다. 해당 어노테이션을 사용하면 SEQUENCE 전략과 같아진다.


AUTO 전략

GenerationType.AUTO는 선택한 DB에 따라 IDENTITY , SEQUENCE , TABLE 전략 중 하나를 자동선택한다. Oracle은  SEQUENCE 를, MySQL은 IDENTITY 를 사용한다.

// 선언 방식

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

@GeneratedValue.strategy 의 기본값은 AUTO이기 때문에 strategy 속성을 사용하지 않아도 위와 같다. AUTO 전략은 DB를 변경해도 코드 수정이 필요 없다. 키생성전략이 정해지지 않은 초기 단계나 프로토타입 개발시 이용하면 편리하다.
그리고 AUTO를 사용시, SEQUENCE나 TABLE 전략을 사용하면 시퀀스나 키 생성용 테이블을 미리 생성해야한다.

지금까지의 매핑방식을 간략히 정리하여 보자면 아래와 같다.

  • 직접할당 : 영속상태를 만들기전 개발자가 직접 식별자 값을 할당해야 함.
  • 자동생성 
    • SEQUENCE : DB 시퀀스에서 시퀀스 값을 조회한 후 영속성 컨텍스트에 저장.
    • TABLE : DB 시퀀스 생성용 테이블에서 시퀀스 값을 조회한 후 영속성 컨텍스트에 저장.
    • IDENTITY : DB에 엔티티를 저장 후 자동 증가한 시퀀스 값을 조회하여 영속성 컨텍스트에 저장. (데이터를 저장해야 시퀀스 값 획득)

 

이번 글에서는 Entity(Class)와 DB Table에 매핑하는 방법에 대해 알아보았다. 다음 글에서는 객체의 참조와 테이블의 외래 키를 매핑하는 객체-테이블 연관관계에 대해 알아보도록하자.

 

참조

 

자바 ORM 표준 JPA 프로그래밍

자바 ORM 표준 JPA는 SQL 작성 없이 객체를 데이터베이스에 직접 저장할 수 있게 도와주고, 객체와 관계형 데이터베이스의 차이도 중간에서 해결해준다. 이 책은 JPA 기초 이론과 핵심 원리, 그리고

book.naver.com

 

반응형

'Spring > Spring JPA' 카테고리의 다른 글

[Spring JPA] JPA 란?  (2) 2021.03.18

댓글