엔티티 매핑

4. 엔티티 매핑

  • 엔티티와 테이블을 매핑하기 위해서 매핑 어노테이션을 사용한다.
    • 객체와 테이블 매핑: @Entity, @Table
    • 기본 키 매핑: @Id
    • 필드와 컬럼 매핑: @Column
    • 연관관계 매핑: @ManyToOne, @JoinColumn
  • 어노테이션 또는 XML을 사용해서 매핑 가능하다. 책에서는 어노테이션만 설명한다.

4.1 @Entity

  • 테이블과 매핑할 클래스에는 @Entity 어노테이션을 필수로 붙여야한다.
  • 기본 생성자가 반드시 있어야 한다.(기본 생성자는 파라미터가 없는 public 또는 protected 생성자이다.)
    • 생성자를 만들지 않으면 기본 생성자가 자동으로 생성된다.
    • 생성자를 하나 이상 만들면 기본 생성자가 자동으로 생성되지 않으므로, 직접 기본 생성자를 만들어야 한다.
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안 된다.
  • 속성 기능 기본값
    name 엔티티 이름을 지정한다. 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다. 클래스 이름

4.2 @Table

  • @Table은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
  • 속성 기능 기본값
    name 매핑할 테이블 이름이다. 엔티티 이름을 사용한다.
    catalog catalog를 매핑한다.  
    schema schema를 매핑한다.  
    uniqueConstraints 유니크 제약조건을 만든다.  
    indexes 인덱스를 생성한다.  

4.3 다양판 매핑 사용

@Entity
@Table(name="MEMBER_TABLE")
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;
    @Column(name = "NAME")
    private String username;

    private Integer age;

    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description;
}
create table MEMBER_TABLE (
    MEMBER_ID bigint not null,
    age integer,
    createdDate timestamp,
    description clob,
    lastModifiedDate timestamp,
    roleType varchar(255),
    NAME varchar(255),
    primary key (MEMBER_ID)
)

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

  • JPA는 클래스의 매핑정보와 데이터베이스 방언을 사용해서 데이터베이스 스키마 자동생성 기능을 지원한다.
  • persistence.xml에 hibernate.hbm2ddl.auto 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.
  • <property name="hibernate.hbm2ddl.auto" value="create"/>
    
  • persistence.xml에 hibernate.show_sql 속성을 추가하면 콘솔에 실행되는 테이블 생성 DDL을 출력한다.
  • <property name="hibernate.show_sql" value="true"/>
    
  • 자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다.
    • Oracle은 VARCHAR2, MySQL은 VARCHAR
    • Oracle은 NUMBER, MySQL은 INT
  • 옵션 설명
    create-only 테이블을 새로 생성한다.
    drop 테이블을 삭제한다.
    create 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
    create-drop create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. DROP + CREATE + DROP
    update 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
    validate 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이 설정은 DDL을 수정하지 않는다.
    none 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.(none은 유효하지 않은 옵션 값이다.)
  • 운영 서버에서 create, create-drop, update처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다.
  • 이름 매핑 전략 hibernate.ejb.​naming_strategy 속성은 hibernate.implicit_naming_strategy 와 hibernate.physical_naming_strategy 로 변경되었다.

4.5 DDL 생성 기능

  • @Column 어노테이션의 nullable 속성을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건이 추가된다.
  • @Column 어노테이션의 length 속성 값을 설정하면 자동 생성되는 DDL에 문자의 크기를 지정할 수 있다.
  • @Table 어노테이션의 uniqueConstraints 속성을 지정하면 유니크 제약조건이 추가된다.
  • @Column의 length, nullable 속성, @Table의 uniqueConstraints 속성 등은 DDL 자동 생성시에만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
@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;
    private Integer age;
}
create table member (
       id varchar(255) not null,
        age integer,
        name varchar(10) not null,
        primary key (id)
    )

add constraint NAME_AGE_UNIQUE unique (name, age)

4.6 기본 키 매핑

  • JPA가 제공하는 데이터베이스 기본 키 생성 전략은 다음과 같다.
    • 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
    • 자동 생성: 대리 키 사용방식
      • IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
      • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
      • TABLE: 키 생성 테이블을 사용한다.
  • 오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.
  • MySQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다.
  • SEQUENCE나 IDENTITY 전략은 데이터베이스에 의존한다.
  • TABLE 전략은 키 생성용 테이블을 만들어 시퀀스처럼 사용하는 방식으로, 테이블을 이용하므로 모든 데이터베이스에서 사용가능하다.
  • hibernate.id.new_generator_mappings 속성은 하이버네이트5 부터 true가 디폴트로 설정된다. https://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators

4.6.1 기본 키 직접 할당 전략

  • @Id로 매핑하면 된다.
  • @Id
    private String id;
    
  • @Id 적용 가능 자바 타입은 다음과 같다.
    • 자바 기본형
    • 자바 래퍼형
    • String
    • java.util.Date
    • java.sql.Date
    • java.math.BigDecimal
    • java.math.BigInteger
  • 기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.
  • Member member = new Member();
    member.setId(1L);
    em.persist(member);
    

4.6.2 IDENTITY 전략

  • 기본 키 생성을 데이터베이스에 위임하는 전략이다.
  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다. 예를 들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
  • @Entity
    public class Member {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    }
    
  • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요한데, IDENTITY 전략은 엔티티를 데이터베이스에 저장해야 식별자를 얻을 수 있다. 따라서 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.
  • IDENTITY 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

4.6.3 SEQUENCE 전략

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 데이터베이스 오브젝트이다.
  • SEQUENCE 전략은 시퀀스를 사용해서 기본 키를 생성한다.
  • SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 식별자를 엔티티에 할당한 후에 영속성 컨텍스트에 저장한다.
  • 속성 기능 기본값
    name 식별자 생성기 이름 필수
    sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence
    initialValue 시퀀스 DDL을 생성할 때 처음 시작하는 수 1
    allocationSize 시퀀스 한 번 호출에 증가하는 수 50
    catalog 데이터베이스 catalog 이름  
    schema 데이터 베이스 schema 이름  
create sequence member_sequence start with 1 increment by 1;
@Entity
@SequenceGenerator(
        name = "member_sequence_generator",
        sequenceName = "member_sequence", // 데이터베이스 시퀀스 이름
        initialValue = 1,
        allocationSize = 1
)
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_sequence_generator")
    private Long id;
    private String username;
    private Integer age;
}

4.6.4 TABLE 전략

  • TABLE 전략은 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스처럼 동작하는 전략이다.
  • 이 전략은 테이블을 사용하므로 모든 데이터베이스에서 사용할 수 있다.
  • 속성 기능 기본값
    name 식별자 생성기 이름 필수
    table 키생성 테이블명 hibernate_sequences
    catalog 데이터베이스 catalog 이름  
    schema 데이터베이스 schema 이름  
    pkColumnName 시퀀스 컬럼명 sequence_name
    valueColumnName 시퀀스 값 컬럼명 next_val
    pkColumnValue 키로 사용할 값 이름 엔티티 이름
    initialValue 초기 값 0
    allocationSize 시퀀스 한 번 호출에 증가하는 수 50
    uniqueConstraints 유니크 제약조건을 지정할 수 있다.  
    indexes 인덱스를 생성한다.  
create table my_sequences (
    sequence_name varchar(255) not null,
    next_val bigint,
    primary key (sequence_name)
)
@Entity
@TableGenerator(
        name = "member_sequence_generator",
        table = "my_sequences", // 데이터베이스 테이블
        pkColumnValue = "member_sequence",
        allocationSize = 1
)
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "member_sequence_generator")
    private Long id;
    private String username;
    private Integer age;
}

4.6.5 AUTO 전략

  • AUTO 전략은 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
  • AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다.
  • SEQUENCE 나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다. 스키마 자동 생성 기능 사용시 자동으로 만들어준다.
@Entity
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private Integer age;
}
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private Integer age;
}

4.6.6 기본 키 매핑 정리

  • 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다.
  • 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다.
  • 직접 할당: em.persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당해야 한다.
  • SEQUENCE: 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • TABLE: 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • IDENTITY: 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
    • 테이블에 데이터를 저장해야 식별자 값을 획득할 수 있으므로, em.persist() 호출 시점에 INSERT SQL이 데이터베이스에 전달되므로 쓰기 지연 기능이 불가능하다.

4.7 필드와 컬럼 매핑: 레퍼런스

4.7.1 @Column

  • 객체 필드를 테이블 컬럼에 매핑한다.
  • 자바 기본 타입에는 @Column을 사용하면 nullable = false로 지정하는 것이 안전하다.
    • 기본 타입에는 null 값을 입력할 수 없다.
      • DDL 자동 생성시 기본 타입에 not null 제약조건을 자동으로 추가한다.
      • 기본 타입에 @Column 사용시 nullable 기본 값이 true로 되어있어 not null 제약조건이 설정되지 않는다.
    • 객체 타입에는 null 값을 입력할 수 있다.
  • 속성 기능 기본값
    name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름
    insertable 엔티티 저장 시 이 필드도 같이 저장한다. false 옵션은 읽기 전용일 때 사용한다. true
    updatable 엔티티 수정 시 이 필드도 같이 수정한다. false 옵션은 읽기 전용일 때 사용한다. true
    table 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다. 지정한 필드를 다른 테이블에 매핑할 수 있다. 현재 클래스가 매핑된 테이블
    nullable(DDL) null 값의 허용 여부를 설정한다. true
    unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 만약 두 컬럼 이상을 사용해서 유니크 제약조건을 사용하려면 클래스 레벨에서 @Table.uniqueConstraints를 사용해야 한다.  
    columnDefinition(DDL) 데이터베이스 컬럼 정보를 직접 줄 수 있다.  
    length(DDL) 문자 길이 제약조건, String 타입에만 사용한다. 255
    precision(DDL) BigDecimal, BigInteger 타입에서 사용한다. 소수점을 포함한 전체 자릿수를 나태난다.  
    scale(DDL) BigDecimal, BigInteger 타입에서 사용한다. 소스의 자릿수를 나타낸다.  

4.7.2 @Enumerated

  • enum 타입을 매핑할 때 사용한다.
  • EnumType.ORDINAL
    • enum 순서를 데이터베이스에 저장
  • EnumType.STRING
    • enum 이름을 데이터베이스에 저장
  • EnumType.STRING 타입을 무조건 쓰도록 권장한다. ORDINAL은 enum 순서가 바뀌면 문제가 발생하기 때문이다.
@Enumerated(EnumType.ORDINAL)
private RoleType roleType;

@Enumerated(EnumType.STRING)
private RoleType roleType;        

4.7.3 @Temporal

  • 날짜 타입(java.uitl.Date, java.util.Calendar)을 매핑할 때 사용한다.
  • TemporalType.DATE
    • 데이터베이스의 date 타입과 매핑
  • TemporalType.TIME
    • 데이터베이스의 time 타입과 매핑
  • TemporalType.TIMESTAMP
    • 데이터베이스의 timestamp 타입과 매핑
@Temporal(TemporalType.DATE)
private Date date;

@Temporal(TemporalType.TIME)
private Date time;

@Temporal(TemporalType.TIMESTAMP)
private Date timestamp;

private LocalDate localDate;
private LocalDateTime localDateTime;
create table Member (
    id bigint not null,
    age integer,
    date date,
    localDate date,
    localDateTime timestamp,
    time time,
    timestamp timestamp,
    username varchar(255),
    primary key (id)
)

4.7.4 @Lob

  • 데이터베이스 BLOB, CLOB 타입과 매핑한다.
  • 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB으로 매핑한다.
  • CLOB: String, char[], java.sql.CLOB
  • BLOB: byte[], java.sql.BLOB

4.7.5 @Transient

  • 이 필드는 매핑하지 않는다.
  • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
@Transient
private int temp;

4.7.6 @Access

  • JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
  • 필드 접근
    • AccessType.FIELD
    • 필드에 직접 접근한다. private이어도 접근할 수 있다.
  • 프로퍼티 접근
    • AccessType.PROPERTY
    • 접근자 Getter를 사용한다.
  • @Access를 설정하지 않으면 @Id의 위치를 기준으로 접근방식이 설정된다.
@Entity
//@Access(AccessType.FIELD)
public class Member {
    @Id @GeneratedValue
    private Long id;
}

@Entity
//@Access(AccessType.PROPERTY)
public class Member {
    private Long id;
    private String username;
    private Integer age;

    @Id @GeneratedValue
    public Long getId() {
        return id;
    }
}

Tags:

Categories:

Updated:

Leave a comment