JPA 엔티티 매핑

023·2024년 8월 8일
0

java persistence api

목록 보기
4/5
post-thumbnail

서론

이 글에서는 JPA의 엔티티 매핑(Entity Mapping)에 대해 설명한다.

1. 객체와 테이블 매핑

1.1. @Entity

  • JPA를 사용하여 테이블과 매핑할 클래스는 반드시 @Entity 어노테이션을 붙여야 하며, @Entity가 붙은 클래스는 JPA가 관리하는 엔티티라고 부른다. @Entity를 적용할 때 주의사항으로는, 파라미터가 없는 public 또는 protected 기본 생성자가 필수이며, final 클래스, enum, interface, inner 클래스에는 @Entity를 사용할 수 없고, 저장할 필드에는 final을 사용할 수 없다.

    속성기능기본값
    nameJPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다.설정하지 않으면 클래스 이름을 그대로 사용한다.

1.2. @Table

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

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

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

  • JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다. 클래스의 매핑 정보를 통해 어떤 테이블에 어떤 컬럼을 사용할지 알 수 있으며, JPA는 이러한 매핑 정보와 데이터베이스 방언을 사용하여 데이터베이스 스키마를 생성한다.

  • hibernate.hbm2ddl.auto 속성

    <property name="hibernate.hbm2ddl.auto" value="create"/>
    • persistence.xml에 다음 속성을 추가하면 데이터베이스 스키마가 자동으로 생성된다.

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

    • HBM2DDL 주의사항

      • 운영 장비에서는 절대 create, create-drop, update 사용하면 안 된다.
      • 개발 초기 단계는 create 또는 update
      • 테스트 서버는 update 또는 validate
      • 스테이징과 운영 서버는 validate 또는 none

2.1. DDL 생성 기능

//not null 제약조건과 문자의 크기를 지정할 수 있다.
@Column(name = "name", nullable = false, length = 10)
private String username;

//유니크 제약조건을 추가할 수 있다.
@Table(name = "MEMBER", uniqueConstraints = {@UniqueConstraint(
	name = "NAME_AGE_UNIQUE",
	columnNames = {"NAME", "AGE"}
)})
public class Member {

    @Id
    private Long id;

    @Column(name = "name")
    private String name;
    
    private Integer age;
    
}
  • 이런 기능들은 단지 DDL을 자동 생성할 때만 사용되며, JPA의 실행 로직에는 영향을 주지 않는다. 따라서 스키마 자동 생성 기능을 사용하지 않고 직접 DDL을 작성한다면 이러한 기능을 사용할 이유가 없다.

3. 필드와 컬럼 매핑

분류 매핑 어노테이션 설명
필드와 컬럼 매핑 @Column 컬럼을 매핑
@Enumerated 자바의 enum 타입을 매핑
@Temporal 날짜 타입을 매핑
@Lob BLOB, CLOB 타입을 매핑
@Transient 특정 필드를 데이터베이스에 매핑하지 않는다.
기타 @Access JPA가 엔티티에 접근하는 방식을 지정
private LocalDate testLocalDate;
private LocalDateTime testLocalDateTime;

이와 같이 LocalDate를 사용하는 경우 @Temporal 매핑 어노테이션이 필요 없다.

3.1. @Column

  • @Column은 객체 필드를 테이블 컬럼에 매핑한다. 이 어노테이션에서 name과 nullable 속성이 주로 사용되며, 나머지 속성들은 잘 사용되지 않는다.

    속성기능기본값
    name필드와 매핑할 테이블의 컬럼 이름객체의 필드 이름
    insertable (거의 사용하지 않음)엔티티 저장시 이 필드도 같이 저장한다. false로 설정하면 이 필드는 데이터베이스에 저장하지 않는다.true
    updatable (거의 사용하지 않음)엔티티 수정 시 이 필드도 같이 수정 false로 설정하면 데이터베이스에 수정하지 않는다.true
    table (거의 사용하지 않음)하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용. 지정한 필드를 다른 테이블에 매핑할 수 있다.현재 클래스가 매핑된 테이블
    nullable(DDL)null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다.true
    unique(DDL)(사용하지않는다. @Table 속성을 사용해서 unique 기능을 사용한다.)@Table의 uniqueContraints와 같지만 한 칼럼에 간단히 유니크 제약조건을 걸 때 사용한다. 만약 두 컬럼 이상을 사용해서 유니크 제약조건을 사용하려면 클래스 레벨에서 @Table.uniqueConstraints를 사용해야 한다.
    columnDefinition(DDL)데이터베이스 컬럼 정보를 직접 줄 수 있다.필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다
    lenght(DDL)문자 길이 제약조건, String 타입에만 사용255
    precision, scale(DDL)BigDemical 타입에서 사용한다.(BigInteger도 사용 할 수 있다). precision은 소수점을 포함한 전체 자릿수를 scale은 소수의 자릿수다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.precision=19, scale=2
    @Column(nullable = false)
    private String data;
    // data varchar(255) not null
    
    @Column(columnDefinition = "varchar(100) default 'EMPTY'")
    private String data;
    // data varchar(100) default 'EMPTY'
    
    @Column(length = 400)
    private String data;
    // data varchar(400)

3.2. @Column 생략

  • @Column을 생략하게 되면 어떻게 될까?

    // 기본 타입에는 null 값을 입력할 수 없다.
    int data1;
    data1 integer not null //DDL
    
    //객체 타입 일 때는 null 값이 허용된다.
    Integer data2;
    data2 integer //DDL
    
    // 
    @Column
    int data3
    data3 integer //DDL

3.3. @Enumerated

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

    속성기능기본값
    valueEnumType.ORDINAL: enum 순서를 데이터베이스에 저장EnumType.STRING: enum 이름을 데이터베이스에 저장EnumType.ORDINAL
  • EnumType.ORDINAL은 enum에 정의된 순서대로 ADMIN은 0, USER는 1 값이 데이터베이스에 저장된다. 이 방식의 장점은 데이터베이스에 저장되는 데이터 크기가 작다는 것이지만, 단점으로는 이미 저장된 enum의 순서를 변경할 수 없다는 점이 있다. 반면, EnumType.STRING은 enum 이름 그대로 ADMIN은 'ADMIN', USER는 'USER'라는 문자로 데이터베이스에 저장된다. 이 방식의 장점은 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다는 것이지만, 단점으로는 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해 크다는 점이 있다. ORDINAL 방식의 단점이 크기 때문에 항상 EnumType.STRING을 사용하는 것이 권장된다.

    public enum OrderStatus {
        ORDER, CANCEL;
    }
    @Enumerated(EnumType.STRING)
    private OrderStatus status;

3.4. @Temporal

  • 날짜 타입(java.util.Date, java.util.Calendar)를 매핑할 때 사용한다.

    속성기능기본값
    valueTemporal.DATE : 날짜, 데이터베이스 date 타입과 매핑
    TemperalType.TIME : 시간, 데이터베이스 time 타입과 매핑
    TemperalType.TIMESTAMP : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑
    TemporalType은 필수로 지정해야 한다.
    @Temporal(TemporalType.DATE) // (ex : 2013-10-11)
    private Date date;
    @Temporal(TemporalType.TIME) // (ex : 11:11:11)
    private Date time;
    @Temporal(TemporalType.TIMESTAMP) // (ex : 2013-10-11 11:11:11)
    private Date timestamp;
    
    //==생성된 DDL==/
    date date,
    time time,
    timestamp timestamp
  • 자바의 Date 타입에는 년월일 시분초가 있지만, 데이터베이스에는 date, time, timestamp(날짜와 시간)라는 세 가지 타입이 별도로 존재한다. @Temporal을 생략하면 자바의 Date와 가장 유사한 DDL이 생성되며, 이는 데이터베이스에 따라 다음과 같이 다르게 매핑된다:

    • datetime : MYSQL
    • timestamp : H2, 오라클, PostgreSQL
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    private LocalDate testLocalDate;
    private LocalDateTime testLocalDateTime;
    • java.time 패키지의 LocalDate와 LocalDateTime을 사용할 때는 @Temperal 어노테이션이 필요 없다.

3.5. @Lob

  • @Lob 어노테이션은 데이터베이스의 BLOB, CLOB 타입과 매핑하는 데 사용된다. @Lob에는 지정할 수 있는 속성이 없으며, 매핑하는 필드 타입이 문자일 경우 CLOB으로 매핑된다. CLOB은 String, char[], java.sql.CLOB 타입과 매핑되고, BLOB은 byte[], java.sql.BLOB 타입과 매핑된다.

    @Lob
    private String lobString;
    @Lob
    private byte[] lobByte;
    
    //오라클
    lobString clob;
    lobByte blob;
    
    //MySQL
    lobString longtext,
    lobByte longblob,

3.6. @Transient

  • @Transient 어노테이션은 "이 필드는 매핑하지 않는다"를 선언해 준다. 즉, 해당 필드는 데이터베이스에 저장되지 않으며, 데이터베이스로부터 조회되지도 않는다.
    @Transient
    private int temp;
  1. 기본 키 매핑
  • 직접 할당 : 기본 키를 애플리케이션에서 직접 할당한다.

    • @Id 어노테이션만 사용하고 개발자가 직접 할당한다.
  • 자동 생성 : 대리 키 사용 방식 - @GeneratedValue 어노테이션을 추가해야 한다.

    • IDENTITY : 기본 키 생성을 데이터베이스에 위임한다.
    • SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
    • TABLE : 키 생성 테이블을 사용한다.
      (1) 기본 키 직접 할당 전략
  • @Id로 매핑한다.

  • @Id 적용 가능 자바 타입

    • 자바 기본형
    • 자바 래퍼형(Wrapper)
    • String
    • java.util.Date
    • java.sql.Date
    • java.math.BigDecimal
    • java.math.BigInteger
    @Id
    private Long id;
    
    Board board = new Board();
    board.setId(1L);
    em.persist(board);

(2) IDENTITY 전략

  • 기본 키 생성을 데이터베이스에 위임하는 전략
  • MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
  • MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
  • IDENTITY 전략은 AUTO_INCREMENT를 사용하여 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구한다.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private static void logic(EntityManager em) {
        Member member = new Member();
        em.persist(member);
        System.out.println(member.getId());
}
  • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다.
  • 하지만 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.
  • 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

(3) SEQUENCE 전략

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.
  • SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다.
  • 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
@Entity
@SequenceGenerator(
        name = "BOARD_SEQ_GENERATOR",
        sequenceName = "BOARD_SEQ", //매핑할 데이터베이스 시퀀스 이름
        initialValue = 1, allocationSize = 1
)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    
}
  • 데이터베이스 시퀀스를 매핑한다.
    • @SequenceGenerator를 사용해서BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록
  • sequenceName 속성의 이름으로 BOARD_SEQ를 지정
    JPA는 이 시퀀스 생성기를 데이터베이스의 BOARD_SEQ 시퀀스와 매핑한다.
  • SEQUENCE 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.
    em.persist() 호출 시 "call next value for BOARD_SEQ" 쿼리가 나간다.

*SequenceGenerator 속성 정리

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

(4) TABLE 전략

  • TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내 내는 전략이다.
  • 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.
  • 실무에서 잘 사용하지 않는다.
    (5) AUTO 전략
  • GenrerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
    • 오라클 : SEQUENCE
    • MySQL : IDENTITY
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    
}
  • @GeneratedValue.strategy의 기본값은 AUTO이다.
    strategy = GenerationType.AUTO 생략 가능!
  • AUTO를 사용할 때 SEQUENCE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다.
  • 만약 스키마 자동 생성 기능을 사용한다면 하이버네이트가 기본값을 사용해서 적절한 시퀀스를 만들어준다.
profile
Get your hands dirty

0개의 댓글