자바 ORM 표준 JPA 프로그래밍(엔티티 매핑)

iy·2024년 11월 18일

Java-JPA

목록 보기
11/12

4. 엔티티 매핑

JPA의 대표 annotation

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

4.1 @Entity

  • JPA를 사용해 테이블과 매핑할 클래스에 붙여줘야 한다.

Entity 사용 시 주의사항
1. 기본 생성자는 필수(파라미터 없는 public, protected 생성자)
2. final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
3. 저장할 필드에 final을 사용하면 안 된다.

4.2 @Table

  • 엔티티와 매핑할 테이블을 지정한다. 만약 생략하게 되면 엔티티와 같은 이름을 테이블 이름으로 사용한다.
속성기능기본값
name매핑할 테이블 이름엔티티 이름을 사용
catalogcatalog 기능이 있는 데이터베이스에서 catalog를 매핑
schemaschema 기능이 있는 데이터베이스에서 schema를 매핑
uniqueConstraints
(DDL)
DDL 생성 시에 유니크 제약 조건을 만든다. 2개 이상의 복합 유니크 제약 조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만든 때만 사용된다.

4.3 다양한 매핑 사용

4.4 데이터베이스 스키마 자동 생성

옵션설명
create기존 테이블을 삭제하고 생로 생성한다.
DROP + CREATE
create-dropcreate 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다.
DROP+CREATE+DROP
update데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 변경 사항만 수정한다.
validate데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 차이가 있으면 경고를 남기로 애플리케이션을 실행하지 않는다.
이 설정은 DDL을 수정하지 않는다.
none자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.
(none은 유효하지 않은 옵션 값이다.)

스키마 자동 생성 기능은 애플리케이션 실행 시점에 데이터베이스 테이블이 자동으로 생성되므로 직접 생성하는 수고를 덜 수 있다.
하지만 스키마 자동 생성 기능으로 만든 DDL은 운영 환경에서 사용할 만큼 환벽하지 않기 때문에 운영 환경이 아닌 개발 환경에서 매핑 정보를 참고하는 정도로만 사용하는 것을 권한다.

특히 update, create-drop, create 처럼 DDL 수정 옵션은 운영 중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있으므로 개발 단계에서만 사용해야 한다.

4.5 DDL 생성 기능

DDL 생성 기능 중 제약 조건을 annotation을 통해 쉽게 추가할 수 있는 기능이 있다.
이 기능들은 DDL을 자동으로 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

4.6 기본 키 매핑

@Entity
public class Member {
	
    @Id
    @Column(name = "ID")
    private String id;
    ...
}

기본키 생성 전략
1. 직접 할당: 기본 키를 애플리케이션에서 직접 할당
2. 자동 생성: 대리 키 사용 방식

  • IDENTITY: 기본 키 생성을 데이터베이스에 위임
  • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당
  • TABLE: 키 생성 테이블을 사용

자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀시를 제공하지 않는다. 대신 MySQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다.
따라서 SEQUENCE나 IDENTITY 전략은 사용하는 데이터베이스에 의존한다. 기본 키를 직접 할당하려면 @Id만 사용하고 자동 생성 전략을 사용하려면 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다.

4.6.1 기본 키 직접 할당 전략

기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.

Board board = new Board();
board.setId("id1");
em.persist(board);

4.6.2 IDENTITY 전략

이 전략은 기본 키 생성을 데이터베이스에 위임하는 전략이다. 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
ex) MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.

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');
IDDATA
1A
2B

테이블 결과를 확인해보면 ID 컬럼 없이 DATA 값만 입력해 INSERT 해줬는데 자동으로 ID 컬럼에 값이 입력된 것을 확인할 수 있다.

// IDENTITY 매핑 코드
@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.id = 1

em.persist()를 호출해서 엔티티를 저장한 직후 할당된 식별자 값을 출력했을 때 할당된 id값이 출력되었다.
출력된 값 1은 저장 시점에 데이터베이스가 생성한 값을 JPA가 조회한 것이다.

❗ 엔티티가 영속 상태가 되려면 식별자가 반드시 필요한데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.
따라서 이 전략은 트랜잭션을 지원하는 쓰기지연은 동작하지 않는다.

4.6.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;
// 시퀀스 매핑 코드
@Entity
@SequenceGenerator(
	name = "BOARD_SEQ_GENERATOR",
    sequeceName = "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 속성의 이름으로 BOARD_SEQ를 지정했는데 JPA는 이 시퀀스 생성기를 실제 데이터베이스의 BOARD_SEQ 시퀀스와 매핑해준다.
다음으로 키 생성 전략을 GenerationType.SEQUENCE로 설정하고 generator = "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());
}

// 출력: board.id = 1

시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식이 다르다.
SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.

반면 IDENTITY는 먼저 엔티티를 데이터베이스에 저장한 후 식별자를 조회해서 엔티티의 식별자에 할당한다.

4.6.4 TABLE 전략

TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.
테이블을 따로 사용하기 때문에 모든 데이터베이스에 적용이 가능하다.

create table MY_SEQUENCE (
	sequence_name varchar(255) not null, // 기본값 이름 변경 가능
    next_val bigint, // 기본값 이름 변경 가능
    primary key(sequence_name)
)
@Entity
@TableGenerator( // 테이블 키 생성기 등록
	name = "BOARD_SEQ_GENEREATOR", //생성기 이름
    table = "MY_SEQUENCE", // 키 생성용 테이블로 매핑
    pkColumnValue = "BOARD_SEQ", allocationSize = 1) 
public class Board {
	
    @Id 
    @GeneratedValue(strategy = GenerationType.TABLE, // TABLE 전략 선택
    	generator = "BOARD_SEQ_GENERATOR") // id 식별자 값은 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당
    private Long id;
    ...
}
private static void logic(EntityManager em) {
	Board board = new Board();
    em.persist(board);
    System.out.println("board.id = " + board.getId());
}

// 출력: board.id = 1

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

MY_SEQUENCES 결과 테이블

sequence_namenext_val
BOARD_SEQ2
MEMBER_SEQ10
PRODUCT_SEQ50
......

@TableGenerator.pkColumnValue에서 지정한 "BOARD_SEQ"가 컬럼명으로 추가된 것을 확인할 수 있다. 이제 키 생성기를 사용할 때마다 next_val 컬럼 값이 증가할 것이다.
MY_SEQUENCE 테이블에 값이 없으면 JPA가 INSERT 하면서 초기화하므로 값을 미리 넣어둘 필요는 없다.

4.6.5 AUTO 전략

데이터베이스 종류에 따라 전략을 선택한다.(Oracle은 SEQUENCE, MySQL은 IDENTITY)

AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 대신 AUTO를 사용하면서 SEQUENCE, TABLE 전략이 선택되면 미리 테이블을 만들어두어야 한다.


📘 참고 도서: 자바 ORM 표준 JPA 프로그래밍

0개의 댓글