Skip to content

Latest commit

 

History

History
383 lines (258 loc) · 11.2 KB

File metadata and controls

383 lines (258 loc) · 11.2 KB

Chapter4. 엔터티 매핑

객체와 테이블 매핑

객체와 테이블 매핑: @Entity, @Table

필드와 컬럼 매핑: @Column

기본 키 매핑: @Id

연관관계 매핑: @ManyToOne,@JoinColumn

4.1@Entity

  • @Entity가 붙은 클래스는 JPA가 관리

  • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

  • 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)

    • JPA가 엔터티 객체를 생성할 때 기본생성자를 사용하므로 이 생성자는 반드시 있어야 한다.

    • 자바는 생성자가 하나도 없으면 기본 생성자를 자동으로 만든다

      public Member(){} //기본 생성자
    • 문제는 생성자를 하나 이상 만들면 자바는 기본 생성자를 자동으로 만들지 않기 때문에 기본 생성자를 직접 만들어야 한다.

    • JPA를 구현하는 라이브러리가 reflection 을 써서.. 기본생성자가 필수다.

  • final 클래스, enum, interface, inner 클래스 사용X

  • 저장할 필드에 final 사용 X

4.2@Table

  • @Table은 엔티티로 매핑할 테이블 지정
    • name : 매핑할 테이블 이름(default : 엔티티 명)
    • catablog : 데이터베이스 catalog 매핑
    • schema : 데이터베이스 schema 매핑
    • uniqueConstrains : DDL생성 시에 유니크 제약 조건 생성

4.3 @Column, 필드와 컬럼 매핑

package hellojpa;

import javax.persistence.*; 
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

@Entity
public class 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;
}

컬럼 매핑

  • @Column : 컬럼 매핑
    • name : 필드와 매핑할 테이블의 컬럼 이름
    • insertable, updatable : 등록, 변경 가능 여부
      • default : TRUE
      • 거의 사용하지 않음
    • nullable : null값 허용 여부, false로 설정하면 DDL생성 시에 not null 제약 조건이 붙음
      • 생성DDL, validate체크로 자주 씀
    • unique(DDL) : @Table의 uniqueContraints와 같지만 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용
      • 현업에서 잘 안 씀
      • Unique key이름이 복잡하게 생성 됨
    • columnDefinition : 필드의 자바 타입과 방언 정보를 사용해 데이터베이스 컬럼 정보를 직접 줄 수 있다 ex) varchar(100) default 'EMPTY'
    • length(DDL) : 문자 길이 제약조건, String 타입에만 사용
      • default : 255
    • precision, scale(DDL) : BigDecimal 타입에서 사용(BigInteger 도)
      • precision은 소수점을 포함한 전체 자 릿수를, scale은 소수의 자릿수 다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정 밀한 소수를 다루어야 할 때만 사용한다.
      • default : precision=19, scale=2

@Column 생략

  • 대부분 @Column 속성의 기본값이 적용되는데, 자바 기본 타입일 때는 nullable 속성에 예외가 있음.

  • 1 : column 생략, 자바 기본 타입 -> not null 추가 됨

    2 : column 생략, 객체 타입 -> null 허용 됨

    3 : column 추가, 자바 기본 타입 -> nullable default가 true라 nullable = false로 지정하는 것이 안전 함.

  • 참고로 primitive 타입 Int, long, ... 은 null을 입력할 수 없음

1
int data1;
data1 integer not null // DDL
2
Integer data2;
data2 integer // DDL
3
@Column
int data3;
data3 integer // DDL

필드 매핑

  • @Temporal : 날짜 타입 매핑

    • @Temporal(TemporalType.DATE) // 날짜
      @Temporal(TemporalType.DATETIME) // 시간
      @Temporal(TemporalType.TIMESTAMP) // 날짜와 시간
    • TemporalType은 필수로 지정해야 함.

    • 최근엔 Type으로 LocalDateTime, LocalDate를 쓰기에 필요 없는 매핑

  • @Enumerated : enum 타입 매핑

    • public enum RoleType{
        ADMIN, USER
      }
      
      @Entity
      public class Member{
      	//  ... 생략
        @Enumerated(EnumType.STRING)
        private RoleType roleType;
      }
    • EnumType에는 STRING, ORDINAL이 있다

    • ORDINAL

      • enum에 정의된 순서대로 ADMIN은 0, USER는 1
      • DB에 저장되는 데이터의 크기가 작다는 장점이 있지만, 이미 저장된 enum의 순서를 변경할 수 없다는 단점이 있다.
      • 이러한 단점으로, 주의해서 사용해야 함.
    • STRING

      • 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다는 장점이 있지만 DB에 저장되는 데이터 크기가 ORDINAL에 비해 크다
  • @Lob : BLOB, CLOB 매핑

    • 큰 콘텐츠 저장 가능

    • CLOB : String, char[], java.sql.CLOB BLOB : byte[], java.sql.BLOB

    • @Lob
      private String description;
      // 회원을 설명하는 필드는 길이 제한이 없다. 따라서 데이터 베이스 VARCHAR 타입 대신에 CLOB 타입으로 저장해야 한다.
  • @Transient : 특정 필드를 컬럼에 매핑하지 않음 (매핑 무시)

    • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용
    • DB에 저장하지도 조회하지도 않는다.

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

  • DDL 을 애플리케이션 실행 시점에 자동 생성
  • 테이블 중심 → 객체 중심
  • 개발 장비에서만 사용할 것
  • 생성된 DDL은 운영서버에서는 사용하지 않거나 적절히 다듬은 후 사용
  • create : 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
  • create-drop : create와 같으나 종료시점에 테이블 (DROP)
  • update : 변경분만 반영 (운영 DB에는 사용하면 안 됨)
  • validate : 엔티티와 테이블이 정상 매핑 되었는지만 확인
  • none : 사용하지 않음

주의할 점

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

4.5 DDL 생성 기능 - 제약 조건

@Column(nullable = false, length = 10)

DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다

이름 매핑 전략

  • hibername.ejb.naming_strategy 속성 사용하면 이름 매핑 전략 변경 가능
  • table, column 가능
@Column(name="role_type") // 언더스코드(_)
String roleType; // 카멜표기법

4.6 기본키 매핑

  • 직접할당 @Id

    • Board board = new Board();
      board.setId("id1"); // 기본 키 직접 할당
      em.persiste(board);
  • 자동 생성 (@GeneratedValue)

    • IDENTITY : 데이터베이스에 위임, MYSQL
    • SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE
      • @SequenceGenerator 필요
    • TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용
      • @TableGenerator 필요
    • AUTO : 방언에 따라 자동 지정, Default값

IDENTITY

  • 기본키 생성을 데이터베이스에 위임
  • 주로 MySQL, PostgreSQL, SQL Server, DB2 에서 사용
  • AUTO_INCREMENT는 데이터베이스에 INSERT SQL 실행 후에 식별자 ID를 알 수 있음
  • Identity 전략은 em.persist() 시점에 즉시 INSERT SQL을 실행하고 DB에서 식별자를 조회한다.
    • 원래 em.persist 에서는 persist context(1차 캐시)에 저장하고 insert는 commit 시점에 한다.
  • 해당 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
@Entity
 public class Member {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
 }

SEQUENCE

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트 (ex, Oracle Sequence)
  • 주로 오라클, PostgreSQL, DB2, H2 에서 사용
  • em.persist(board); 를 할 때 시퀀스를 호출한다
  • @SequenceGenerator
    • 속성
      • name : 식별자 생성기 이름, 필수
      • sequenceName : DB에 등록되어있는 시퀀스 이름, defalut hibernate_sequence
      • initialValue : DDL생성 시에만 사용됨, 시퀀스 DDL을 생설할 때 처음 1 시작하는 수를 지정한다, default 1
      • allocationSize : 시퀀스 한 번 호출에 증가 하는 수(성능 최적화에 사용됨) 데이터 베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다, default가 50
      • 기타
        • catalog, schema
    • 주의할 점
      • allocationSize 기본값이 50이라는 점
@Entity
@SequenceGenerator(
    name = “MEMBER_SEQ_GENERATOR",
    sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
    initialValue = 1, allocationSize = 1)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE,
  private Long id;
}

Identity VS Sequence 내부 동작 방식

  • Sequence
    • persist => 식별자(id) 조회
    • entity에 식별자를 할당 후 persist (context에 저장)
    • commit -> fulsh -> DB에 저장
  • Identity
    • persist => insert
    • 식별자(id) 조회
    • entity에 식별자 할당

TABLE

  • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
  • 모든 데이터베이스에 적용 가능하다는 장점이 있지만 성능이 단점이다.
    • 키 값을 조회 하면서 select 쿼리를 사용하고 다음 값으로 증가 시키기 위해 Update 쿼리를 사용한다. DB와 한 번 더 통신하는 단점이 있다. 최적화 하는 방법은 SEQUENCE 전략과 같다.(allocationSize사용)
  • @TableGenerator
    • 속성
      • name, table
      • pkColumnName : 시퀀스 컬럼명, default sequence_name
      • valueColumnNa : 시퀀스 값 컬럼명, default next_val
      • pkColumnValue : 키로 사용할 값 이름, default 엔티티 이름
      • initialValue : 초기 값, 마지막으로 생성된 값이 기준이다. default 0
      • allocationSize : 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨), default 50
      • 기타
        • catalog, schema: 데이터베이스 catalog, schema 이름 uniqueConstraints(DDL) : 유니크 제약 조건 지정 가능
create table MY_SEQUENCES (
  sequence_name varchar(255) not null,
  next_val bigint,
  primary key ( sequence_name )
)
@Entity
@TableGenerator(
    name = "MEMBER_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = “MEMBER_SEQ", allocationSize = 1)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE,
  generator = "MEMBER_SEQ_GENERATOR")
  private Long id;

권장하는 식별자 전략

  • 기본 키 제약 조건: null 아님, 유일, 변하면 안된다
  • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자. 예를 들어 주민등록번호도 기본 키로 적절하기 않다.
  • 권장: Long형 + 대체키 + 키 생성전략 사용