본문 바로가기
Study/자바 ORM 표준 JPA 프로그래밍

4장. 엔티티 매핑

반응형

본 포스트는 사내 스터디로 진행되는 자바 ORM 표준 JPA 프로그래밍 관련 정리입니다

 

인프런 강의도 있습니다! 짱짱

https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

www.inflearn.com


4장. 엔티티 매핑

  • 매핑 정보는 XML, 어노테이션 중에 선택해서 기술 하면 된다
  • 각기 장단점이 있지만, 어노테이션을 사용하는 쪽이 좀 더 쉽고 직관적이다
분류 어노테이션
객체와 테이블 매핑 @Entity
@Table
기본 키 매핑 @Id
필드와 컬럼 매핑 @Column
연관관계 매핑 @ManyToOne
@JoinColumn

 

4-1. @Entity

  • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야한다
@Entity 속성 설명 기본값
name JPA에서 사용할 엔티티 이름을 지정
보통 기본값인 클래스 이름을 사용
설정하지 않으면 클래스 이름을 그대로 사용
  • 기본 생성자는 필수 ( 최소 protected 이상의 생성자를 가져야 한다 )
  • final, enum, interface, inner 클래스에는 사용할 수 없다
  • 저장할 필드에 final을 사용하면 안된다

 

4-2. @Table

  • 엔티티와 매핑할 테이블을 지정한다
  • 생략시 매핑한 엔티티(@Entity) 이름을 테이블 이름으로 사용
@Table 속성 설명 기본값
name 매핑할 테이블 이름 엔티티 이름을 사용한다
catalog catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다  
scheme schema 기능이 있는 데이터베이스에서 schema를 매핑한다  
uniqueConstraints(DDL) DDL 생성 시에 유니크 제약조건을 만든다
스키마 자동 생성 기능을 사용해서 DDL을 만들때만 적용
 

 

4-3. 데이터베이스 스키마 자동 생성

  • JPA는 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성할 수 있다
  • 자동으로 생성되는 DDL은 데이터베이스 방언에 따라 달라진다
  • 스키마 자동 생성 기능이 만든 DDL은 운영 환경에서 사용할 만큼 완벽하지는 않으므로 개발 환경에서 사용하거나 매핑 참고용 정도로 사용하는 것이 좋다
옵션 설명
create 기존 테이블을 삭제하고 새로 생성한다
drop + create
create-drop create 속성에 추가로 어플리케이션을 종료시 생성한 DDL을 제거
drop + create + drop
update 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다
validate 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 어플리케이션을 실행하지 않음
none hibernate,hbm2ddl.auto 속성 자체를 유효하지 않은 옵션 값으로 설정
  • 이름 매핑 전략 변경하기
    • 단어와 단어를 구분할 때 자바는 관례상 카멜(Camel) 표기법을 주로 사용
    • 데이터 베이스는 관례상 언더 스코어('_')를 주로 사용
    • 방법
      • 각 어노테이션(@Entity, @Table, @Column 등)의 name 속성에 명시적으로 데이터베이스 관련 이름을 지정해줄 수 있다
      • hibernate.ejb.naming_strategy 속성을 사용해 이름 매핑 전략을 변경할 수 있다

 

4-4. DDL 생성 기능

  • @Column 매핑 정보의 nullable 속성 값을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건을 추가할 수 있다
  • length 속성 값을 사용하면 자동 생성되는 DDL에 문자의 크기를 지정 할 수 있다 ( 기본값 255 )
  • 위 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다

 

4-5. 기본 키 매핑

  • 데이터베이스마다 기본 키를 생성하는 방식(오라클 시퀀스 오브젝트, MySQL AUTO_INCREMENT 등)이 다르므로 이 문제를 해결하기 위해 다양한 옵션을 제공한다
  • JPA가 제공하는 데이터베이스 기본 키 생성 전략
    • 직접 할당: 기본 키를 어플리케이션에 직접 할당 ( @Id )
    • 자동 생성: 대리 키 사용 방식 ( @Id + @GeneratedValue )
      • IDENTITY: 기본 키 생성을 데이터 베이스에 위임
      • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당
      • TABLE: 키 생성 테이블을 사용

 

4-5-1. 기본 키 직접 할당 전략

  • 기본 키를 직접 할당하려면 @Id 어노테이션만 사용하여 매핑하면 된다
  • entityManager.persist()로 엔티티를 저장하기 전에 어플리케이션에서 기본 키를 직접 할당하는 방법
  • 적용 가능 타입
    • 자바 기본형 ( primitive type, [int, long, double....] )
    • 자바 래퍼형 ( Wrapping type, [Integer, Long, Double.....] )
    • String
    • java.util.Date
    • java.sql.Date
    • java.math.BigDecimal
    • java.math.BigInteger
  • 기본 키 직접 할당 전략에서 식별자 값 없이 저장하면 예외가 발생, 어떤 예외가 발생하는지는 JPA 표준에 정의가 되지 않고 하이버네이트를 구현체로 사용하면 JPA 최상위 예외인 javax.persistence.PersistenceException 예외가 발생

 

4-5-2. IDENTITY 전략

  • 기본 키 생성을 데이터베이스에 위임하는 전략
  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다
  • 데이터베이스에 값을 저장할 때 ID 컬럼을 비워두면 데이터베이스가 순서대로 값을 채워준다
    ( 즉 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용 )
  • 직접 할당이 아닌 경우 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다
    ( @GeneratedValue(srategy=GenerationType.IDENTITY) )
  • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다
    • IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist() 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다
    • 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다

 

4-5-3. SEQUENCE 전략

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다
  • 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용 가능
// Sequence Mapping
@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;
    
    ....
}
  • @SequenceGenerator를 사용해서 "BOARD_SEQ_GENERATOR"라는 시퀀스 생성기를 등록
  • 속성 중 sequenceName의 이름으로 실제 데이터베이스의 시퀀스와 매핑한다
  • @GeneratedValue 와 같이 @Id에 사용해도된다 ( Class단 말고 )
  • 동작 방식
    • em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회
    • 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장
    • 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장
  • @SequenceGenerator
    속성 설명 기본값
    name 식별자 생성기 이름 필수
    sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence
    initialValue DDL 생성 시에만 사용됨
    시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정
    1
    allocationSize 시퀀스 한 번 호출에 증가하는 수( 성능 최적화에 사용됨 ) 50
    catalog, schema 데이터베이스 catalog, schema 이름  
  • SEQUENCE 전략과 최적화
    • SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하기 때문에 2번의 통신이 필요
      1. 식별자를 구하려고 데이터베이스 시퀀스를 조회
      2. 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장
    • JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize를 사용한다
      • allocationSize로 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다
      • 시퀀스 값을 어플리케이션 단에서 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값이 충돌하지 않는다
      • 시퀀스 값이 한번에 많이 증가한다는 단점은 존재

 

4-5-4. TABLE 전략

  • 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략
  • 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다
// table mapping
@Entity
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ",
    allocationSize = 1)
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
                    generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    
    ....
}
  • 시퀀스 대신 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 같다
  • "MY_SEQUENCES" 테이블에 "BOARD_SEQ" 컬럼이 추가되고, 키 생성기를 사용할 때마다 해당 컬럼 값이 증가
  • "BOARD"SEQ" 컬럼 값이 없다면 JPA가 INSERT하면서 초기화한다
  • @TableGenerator
    속성 기능 기본값
    name 식별자 생성기 이름 필수
    table 키 생성 테이블명 hibernate_sequences
    pkColumnName 시퀀스 컬럼명 sequence_name
    valueColumnName 시퀀스 값 컬럼명 next_val
    pkColumnValue 키로 사용할 값 이름 엔티티 이름
    initialValue 초기 값
    마지막으로 생성된 값이 기준
    0
    allocationSize 시퀀스 한 번 호출에 증가하는 수
    (성능 최적화에서 사용)
    50
    catalog, schema 데이터베이스 catalog, schema 이름  
    uniqueConstraints(DDL) 유니크 제약 조건을 지정할 수 있다  
  • TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다
    TALBE 전략을 최적화하려면 SEQUENCE 전략과 마찬가지로 allocationSize를 사용하여 가능

 

4-5-5. AUTO 전략

  • 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다
  • 예로 오라클은 SEQUENCE, MySQL은 IDENTITY를 사용한다
  • @GeneratedValue의 strategy default 값
  • 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 장점이 있다 ( 아직 키 생성 전략이 정해지지 않았을 때 편리 )

 

4-5-6. 기본 키 매핑 정리

  • 영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 영속 상태가 되려면 반드시 식별자 값(@Id)가 있어야 한다
  • 권장하는 식별자 선택 전략
    • 데이터베이스 기본 키는 다음 3가지 조건을 만족해야한다
      1. null 값은 허용하지 않는다
      2. 유일해야 한다
      3. 변해선 안된다
    • 테이블의 기본 키를 선택하는 전략
      • 자연 키: 비즈니스에 의미가 있는 키 ( 예: 주민등록번호, 이메일, 전화번호 등등 )
      • 대리 키: 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다 ( 키 생성 테이블, auto_increment, 시퀀스 )
    • 자연 키 보다는 대리 키를 권장한다
      • 두 가지 다 장단점이 있지만, 비즈니스 로직과 연관된 자연 키를 사용할 경우 나중에 벌어질 상황에 대한 대처가 힘들다
      • 현재는 물론이고 미래까지 충족하는 자연 키를 찾기는 쉽지 않다
      • 대리 키는 비즈니스와 무관한 임의의 값이므로 요구사항이 변경되도 기본 키가 변경되는 일은 드물다
    • 기본 키는 변하면 안 된다는 기본 원칙으로 인해, 저장된 엔티티의 기본 키가 변경되면 JPA는 예외를 발생시키거나 정상 동작을 하지 않는다 ( set method를 제공하지 않는 방식이 좋다 )

 

4-6. 필드와 컬럼 매핑

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

 

4-6-1. @Column

  • @Column은 객체 필드를 테이블 컬럼에 매핑한다
속성 기능 기본값
name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름
insertable
(거의 사용하지 않음)
엔티티 저장 시 이 필드도 같이 저장한다
false로 설정시 해당 필드는 데이터베이스에 저장하지 않는다
false 옵션은 읽기 전용일 때 사용
true
updatable
(거의 사용하지 않음)
엔티티 수정 시 해당 필드도 같이 수정
false로 설정시 데이터베이스에 수정하지 않는다
false 옵션은 읽기 전용일 때 사용
true
table
(거의 사용하지 않음)
하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용
지정한 필드를 다른 테이블에 매핑할 수 있다
현재 클래스가 매핑된 테이블
nullable
(DDL)
null 값의 허용 여부를 설정한다
false로 설정시 not null 제약조건이 붙는다
true
unique
(DDL)
@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용  
columnDefinition
(DDL)
데이터베이스 컬럼 정보를 직접 줄 수 있다 필드의 자바 타입과 방언 정보를 사용,
적절한 컬럼 타입을 자동 생성
length
(DDL)
문자 길이 제약 조건, String 타입에만 해당 255
precision, scale(DDL) BigDecimal 타입(BigInteger도 가능)에서 사용
precision은 소수점을 포함한 전체 자릿수
scale은 소수의 자릿수
double, float 타입에는 적용되지 않는다
precision=19,
scale=2 
  • nullable
    # nullable
    
    // java
    @Column(nullable = false)
    private String data;
    
    
    // SQL
    data varchar(255) not null
  • unique
    // unique
    
    // java
    @Column(unique = true)
    private String username;
    
    
    // SQL
    alter table tableName add constraint UK_Xxx unique (username)​
  • columnDefinition(DDL 생성 기능)
    // columnDefinition
    
    // java
    @Column(columnDefinition="varchar(100) default 'EMPTY'")
    private String data;
    
    // SQL
    data varchar(100) default 'EMPTY'​
  • length(DDL 생성 기능)
    // length
    
    // java
    @Column(length = 400)
    private String data;
    
    // SQL
    data varchar(400)​
  • precision, scale(DDL 생성 기능)
    // precision, scale
    
    // java
    @Column(precision = 10, scale = 2)
    private BigDecimal cal;
    
    
    // SQL
    cal numeric(10,2) // h2, postgreSQL
    cal number(10,2) // oracle
    cal decimal(10,2) // MySQL​
  • @Column을 생략하게 되면?
    • 대부분 @Column 속성들은 기본값(default)이 적용되는데 nullable 속성에는 예외가 있다
    • @Column 속성을 생략 후 자바 기본 타입(primitive type)을 쓰게 되면 자동으로 not null 조건이 붙게 된다
    • 단 @Column 속성 + 자바 기본 타입일 땐 nullable 속성의 기본값(true)로 동작
      // nullable
      
      // #1
      int data1; 
      => data1 integer not null
      
      // #2
      Integer data2;
      => data2 integer
      
      // #3
      @Column
      int data3
      => data3 integer​

 

4-6-2. @Enumerated

  • 자바의 enum 타입을 매핑할 때 사용
속성 분류 설명 기본값
value EnumType.ORDINAL enum 순서를 데이터베이스에 저장  EnumType.ORDINAL
EnumType.STRING enum 이름(name)을 데이터베이스에 저장
  • EnumType.ORDINAL
    • 장점: 데이터베이스에 저장되는 데이터 크기가 작다
    • 단점: 이미 저장된 enum의 순서를 변경할 수 없다
  • EnumType.STRING
    • 장점: 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다
    • 단점: 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해서 크다

 

4-6-3. @Temporal

  • 날짜 타입(java.util.Date, java.util.Calendar)를 매핑할 때 사용
  • Spring Data JPA를 사용하고, java8이상을 쓴다면.. 잘 안쓸거 같다 ( LocalDateTime 사용 )
속성 분류 설명 기본값
value TemporalType.DATE 날짜, 데이터베이스 date 타입과 매핑
( 2013-10-11 )
TemporalType은 필수로 명시적인 지정이 필요하다
TemporalType.TIME 시간, 데이터베이스 time 타입과 매핑
( 11:11:11 )
TemporalType.TIMESTAMP 날짜와 시간, 데이터베이스 timestamp 타입과 매핑
( 2013-10-11 11:11:11 )
  • 데이터 베이스 방언에 따라 생성되는 DDL
    • datetime: MySQL
    • timestamp: H2, 오라클, PostgreSQL

 

4-6-4. @Lob

  • 데이터베이스 BLOB, CLOB 타입과 매핑
  • @Lob에는 지정할 속성은 없고, 매핑하는 필드 타입에 따라 문자면 CLOB, 나머지는 BLOB으로 매핑한다
  • CLOB
    • String
    • char[]
    • java.sql.CLOB
  • BLOB
    • byte[]
    • java.sql.BLOB

 

4-6-5. @Transient

  • 이 필드는 매핑하지 않는다
  • 데이터베이스에 저장하지 않고, 조회하지도 않는다
  • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용

 

4-6-6. @Access

  • JPA가 엔티티 데이터에 접근하는 방식을 지정
  • @Access를 설정하지 않으면 @Id 어노테이션의 위치를 기준으로 접근 방식이 설정된다
  • 필드 접근
    • AccessType.FIELD
    • 필드에 직접 접근
    • 필드 접근 지정자가 private이어도 접근할 수 있다
  • 프로퍼티 접근
    • AccessType.PROPERTY
    • 접근자(getter method)를 사용
728x90
반응형

'Study > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글

6장. 다양한 연관관계 매핑  (0) 2021.09.13
5장. 연관관계 매핑 기초  (0) 2021.09.04
3장. 영속성 관리  (0) 2021.08.22
1장. JPA 소개  (0) 2021.08.09