JPA의 Entity Mapping

상윤·2024년 5월 12일
0

BackEnd

목록 보기
6/11

JPA 프로그래밍 - 김영한
JPA를 사용하데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다.
따라서 매핑 어노테이션을 숙지하고 사용해야 한다.

대표 어노테이션 정리

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

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

name : 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용. 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다. (설정하지 않으면 기본값으로 클래스 이름을 그대로 사용한다. ex) Member)

주의 사항
-기본 생성자는 필수
-final 클래스, enum, interface, inner 클래스에는 사용 불가능
-저장할 필드에 final 을 사용하면 안된다.

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

catalog catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.
schema schema 기능이 있는 데이터베이스에서 schema를 매핑한다.
uniqueConstraints DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다.(스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.)

매핑사용 예제

요구 사항
1 회원은 일반 회원과 관리자로 구분
2 회원 가입일과 수정일이 있어야 한다.
3 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.

기존의 회원 엔티티

package jpabook.start;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name="MEMBER")
public class Member {
    @Id
    @Column(name = "ID”)
    private String id;
    @Column(name = "NAME") 
    private String username;
    private Integer age;

    //== 추가 ==
    @Enumerated(EnumType.STRING) private RoleType roleType; 
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description;

    //Getter, Setter
...

}

package jpabook.start;

public enum RoleType {
        ADMIN, USER
}

코드 분석
roleType : 자바의 enum 을 사용해서 회원 타입을 구분(java의 enum을 사용하려면 @Enumerated 어노테이션으로 매핑 필요)
createDAte,lastModifiedDate:자바의 날짜 타입은 @Temporal을 사용해서 매핑.
description : 회원을 설명하는 필드는 길이 제한이 없으므로, DB의 VARCHAR 타입 대신에 CLOB 타입으로 저장해야 함. @LOB 어노테이션 사용 시, CLOB,BLOB 타입 매핑 가능

이름 매핑 전략
자바는 카멜 표기법을 주로 사용하고 DB는 언더스코어"_"를 주로 사용하기 때문에 이름 매핑 전략을 사용한다.

@Column(name = "role_type")
String role Type 

DDL 생성

지금까지는 테이블 생성 후 엔티티를 만들었지만, JPA의 데이터베이스 스키마 자동완성을 사용해서 엔티티만 만들고 테이블을 자동 생성 할 수 있다.

  jpa:
    open-in-view: true
    hibernate:
      ddl-auto: create
    #show-sql: true
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 100
        open-in-view: false

yml 파일에서 ddl-auto 옵션을 create로 주고, show-sql 옵션을 true로 주면 어플 실행 시 콘솔에서 DDL이 출력되며 자동으로 테이블을 생성한다.

출력 예시

Hibernate:
	drop table MEMBER if exists 
Hibernate:
	create table MEMBER (
      ID varchar(255) not null,
      NAME varchar(255),
      age integer,
      roleType varchar(255),
      createdDate timestamp, 
      lastModifiedDate timestamp,
      description clob,
      primary key (ID) 
	)

어노테이션에 맞춰 타입이 생성된 것을 확인 할 수 있다. 또한 자동 생성되는 DDL은 DB에 맞춰 구문이 작성된다. ex) oracle DB 사용 시 number 타입 생성

ddl-auto 속성 정리

create
기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE

create-drop
create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다.
DROP + CREATE + DROP

update
데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.

validate
데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남 기고 애플리케이션을 실행하지 않는다. 이 설정은 DDL을 수정하지 않는다.

none
자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭 제하거나 유효하지 않은 옵션 값을 주면 된다(참고로 none은 유효하지 않은 옵 션 값이다

운영 서버에서 DDL 수정 옵션은 절대 사용하면 안된다.(운영 중인 데이터베이스의 테이블이나 컬럼을 삭제 할 수 있다.)
개발 초기 단계 : create or update
초기화 상태로 자동화된 테스트 진행하는 개발자 환경 혹은 CI 서버 : create or create-drop
테스트 서버 : update or validate
운영 및 스테이징 서버 : validate or none

제약조건
@Column 매핑정보의 nullable 속성 값을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건 추가 가능. 또한 lenght 속성 값을 사용하면 자동 생성되는 DDL에 문자 크기를 지정할 수 있다.

@Column(name = "NAME", nullable = false, length = 10) 
private String username;

테이블의 유니크 제약조건 uniqueConstraints

@Entity(name="Member")
@Table (name="MEMBER" , uniqueConstraints = {@UniqueConstraint(
	name = "NAME_AGE_UNIQUE", 
    columnNames = {"NAME", "AGE”} )})
    
   public class Member {...} 
   
   생성된 DDL
ALTER TABLE MEMBER
ADD CONSTRAINT NAME_AGE_UNIQUE UNIQUE (NAME, AGE)

DDL 제약 조건은 자동생성 할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다.
하지만 이 기능을 사용한다면 개발자는 엔티티만 보고도 손쉽게 다양한 제약조건을 파악 할 수 있다는 장점이 있다.

Primary Key - 기본 키 매핑

이전 예제처럼 @Id 어노테이션만 사용해서 회원의 기본키를 어플이케이션에 직접 할당했다.
기본 키를 app에서 직접 할당하는 대신 데이터 베이스가 생성해주는 값을 사용하려면 어떻게 매핑해야 할까?
오라클의 시퀀스 오브젝트라던가, MySQL의 AUTO_INCREMENT 같은 기능을 사용해서 생성된 값을 기본키로 사용하는 JPA의 전략을 알아보자

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

자동 생성이 다양한 이유는 데이터베이스의 벤더마다 지원 방식이 다르기 때문.
TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스 처럼 사용하기 때문에 모든 데이터 베이스에서 사용 가능.
기본 키를 자동 생성 전략 사용 방법은 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다.

IDENTITY 전략
PK 생성을 데이터베이스에 윈하는 전략이다. 주로 MySQL, PostgreSQL, SQL server, DB2 에서 사용된다. 예를 들어 MySQL의 AUTO_INCREMENT 기능을 수행 시 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다. IDENTITY 전략을 사용하려면 @GeneratedVAlue의 strategy 속성값을 GenerationType.IDENTITY로 지정하면 된다. 이 때 JPA 는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회한다.
(IDENTITY 전략은 데이터베이스에서 INSERT 한 후 기본키 값을 조회 할 수 있기 때문에, 데이터 저장 후 동시에 조회해 id 값을 갖고 오기 때문에 한번의 통신으로 키값을 갖고 올 수 있다. 또한 영속 상태가 되려면 식별자가 반드시 필요하다. 엔티티를 DB에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT query가 전달되어 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.)

SEQUENCE 전략
주로 ORACLE,PostgreSQL,DB2,H2 데이터베이스에서 사용할 수 있다.
@GeneratedValue(strategy = GenerationType.SEQUENCE, 시퀀스 객체)를 사용한다

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

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

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

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

데이터베이스를 변경해도 코드를 수정할 필요가 없다는 장점이 있다. 키생성 전략이 확정되지 않은 초기 단꼐나 프로토타입 개발 시 편리하게 사용할 수 있다.

기본키 매핑 정리

영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다. em.persist()를 호출한 직후에 발생하는 일은 식별자 할당 전략별로 정리하면 다음과 같다.

직접 할당 : em.persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당해야함. 없을 시 예외 발생
SEQUENCE : 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장.
TABLE: 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후, 영속성 컨텍스트에 저장.
IDENTITY: 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장

필드와 컬럼 매핑

@Column : 컬럼을 매핑
자주쓰는 속성
nullable : nullable null값의 허용 여부 설정(기본값 true)
unique : 테이블의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용. 만약 두 컬럼 이상한다면 클래스 레벨에서 uniqueConstraints를 사용해야 한다.
columnDefinition : 데이터베이스 컬럼 정보를 직접 줄 수 있다.
length : 문자와 길이 제약조건, String 타입에만 사용
precision,scale : BigDecimal 타입에 사용. precision은 소수점을 포함한 전테 자릿수, scale은 소수의 자릿수. double,float 타입엔 적용되지 않고 아주 큰 숫자나 정밀한 소수를 다루어야할 때 사용.

@Enumerated : enum 타입을 매핑
@Temporal : 날짜 타입을 매핑
@LOB : BLOB, CLOB 타입을 매핑
@Transient : 특정 필드를 데이터베이스에 매핑하지 않음
@Access : JPA가 엔티티에 접근하는 방식을 지정

0개의 댓글