응애개발자
article thumbnail
Published 2023. 7. 11. 14:19
엔티티 매핑 Spring/JPA
728x90

엔티티 어노테이션 4가지

  1. 객체와 테이블 매핑 : @Entitiy, @Table
  2. 기본 키 매핑 : @Id
  3. 필드와 컬럼 매핑 : @Column
  4. 연관관계 매핑 : @ManyToOne, @JoinColumn

 

1. @Entity

JPA를 사용하여 테이블과 매핑할 클래스는 @Entitiy어노테이션을 필수로 붙어야 한다. @Entity는 JPA가 관리하는 것

속성 기능 기본값
name JPA에서 사용할 엔티티 이름을 지정.보통 기본값인 클래스 이름 사용. 다른 패키지에 같은 이름의 엔티티 클래스가 생기지 않도록 주의 설정하지 않으면 클래스 이름 그대로 사용한다.

-주의 사항

  • 기본 생성자는 필수(public, protected)
  • final 클래스, enum, interface, inner 클래스 사용 x
  • 저장할 필드에 final 사용 x

JPA는 엔티티 객체를 생성할 때 기본 생성자를 사용하므로 생성자가 하나도 없으면 기본 생성자를 사용한다. 다른 파라미터로 만든 생성자가 있을 시에 기본 생성자를 직접 만들어야 한다.

2. @Table

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

속성 기능 기본값
name 매핑할 테이블 이름 엔티티 이름을 사용한다
catalog catalog 기능이 있는 DB에서 catalog를 매핑한다  
schema schema 기능이 있는 DB에서 schema를 매핑한다.  

 

3. 다양한 매핑 사용

회원 관리 프로그램에 다음 요구사항을 추가해보자.

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

  1. roleType : 자바의 enum을 사용하여 회원의 타입을 구분했다. 이처럼 enum을 사용하려면 @Enumerated 어노테이션으로 매핑해야한다.
  2. createDate, lastModifiedDate : 자바의 날짜 타입은 @Temporal을 사용하여 매핑한다.
  3. description : 필드는 길이 제한이 없으므로 DB는 VARCHAR 대신 CLOB타입으로 저장해야 한다. @Lob을 사용하면 CLOB, BLOB 타입을 매핑 할 수 있다.

지금까지는 테이블을 먼저 만들고 엔티티를 만들었지만, 이번에는 DB 스키마 자동 생성을 사용하여 엔티티만 만들고 테이블은 자동 생성 되도록 해보자.


4. DB스키마 자동 생성

JPA는 DB 스키마를 자동으로 생성하는 기능을 지원한다.

persistence.xml

먼저 persistence.xml 에 다음 속성을 추가하자. 이 속성을 추가하면 애플리케이션 실행 시점에 DB 테이블을 자동으로 생성한다.

 

*참고 : hibernate.show_sql 속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다.

DDL 콘솔 출력

 

애플리케이션을 실행하면 위 처럼 DDL이 출력 되면서 실제 테이블이 생성된다. 위의 결과를 보면 기존 테이블을 삭제하고 다시 생성한 것을 볼 수 있고, 방금 추가한 roleType은 VARCHAR, createdDate, lastModifiedDate는 TIMESTAMP, description은 CLOB 타입으로 생성 되었다. 이것은 DB의 방언에 따라 달라진다.

*스키마 자동 생성은 수고를 덜어 줄 수 있지만 자동생성 기능이 만든 DDL은 완벽하지 않으므로 어떻게 매핑을 해야하는지 참고하는 정도로만 사용하는 것이 좋다.

 

- hibernate.hbm2ddl.auto 속성

옵션 설명
create 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
create-drop DROP + CREATE + DROP
update DB 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정
validate DB 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 실행하지 않음. 이 설정은 DDL 수정하지 않는다.
none 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto를 삭제하거나 none 값을 주면 된다.

*주의 사항 : 운영 서버에서 create, create-drop, update처럼 DLL을 수정하는 옵션은 절대로 안된다. 오직 개발 서버나 개발 단계에서만 사용해야 함. 이 옵션들은 운영 중인 DB 테이블이나 컬럼을 삭제할 수 있다.

 

- 추천 전략

  • 개발 초기 : create 또는 update
  • 초기화된 상태, CI 서버는 : create 또는 create - drop
  • 테스트 서버 : update 또는 validate
  • 스테이징 운영 서버 : validate 또는 none

*이름 매핑 전략 변경하기

@Column(name = "role_type") //언더스코어로 구분

String roleType //카멜 표기법으로 구분

이렇게 직접 이름 매핑전략을 구현해서 사용 하거나

<property name = "hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" /> 를 사용하면 테이블의 언더스코어 표기법으로 매핑된다.

 

5. DDL 생성 기능

- 추가 기능 사항

  • 회원 이름은 필수, 10글자 초과하면 안된다.

nullable 속성 값을 false로 지정하면 자동 생성 되는 DDL에 not null 제약조건을 추가할 수 있다. 그리고 length 속성 값을 사용하면 문자 크기를 지정할 수 있다.

생성된 DDL


- 추가 기능 사항

  • 유니크 제약조건을 넣어보자

* UNIQUE

UNIQUE 제약 조건을 설정하면, 해당 필드는 서로 다른 값을 가져야 합니다.

즉, 이 제약 조건이 설정된 필드는 중복된 값을 저장할 수 없습니다.

 

유니크 제약 조건

 

생성된 DDL

*@Column의 length와 nullable 속성을 포함한 이런 기능들은 JPA로직에는 영향을 주지 않는다. 따라서 DDL을 직접 만든다면 사용할 이유가 없다.

 

6. 기본 키 매핑

기본 키 매핑 시작

이렇게 @Id 어노테이션만 사용하면 기본 키를 애플리케이션에서 직접 할당했다. 하지만 오라클의 시퀀스 오브젝트라던가 아니면 MySQL의 AUTO_INCREMENT 같은 기능을 사용해서 생성된 값을 기본키로 사용하려면 다음과 같이 해결해보자.

 

- JPA가 제공하는 DB 기본 키 생성 전략

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

-자동 생성 전략이 다양한 이유

벤더마다 지원하는 방식이 다르기 때문에 ex) 오라클 DB는 시퀀스, MySQL은 시퀀스를 제공하지 x, 대신 AUTO_INCREMENT 기능을 제공한다.

따라서 SEQUENCE나 IDENTITY 전략은 사용하는 DB에 의존한다. TABLE 전략은 키 생성용 테이블을 하나 만들어 두고 마치 시퀀스처럼 사용하는 방법. 이 전략은 테이블을 활용하므로 모든 DB에서 사용할 수 있다.

 

기본 키 직접 할당 전략

- @Id 적용 가능 자바 타입

  • 자바 기본형
  • 자바 래퍼(Wrapper)형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

기본 키 직접 할당은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.

 

IDENTITY 전략

IDENTITY는 기본 키 생성을 DB에 위임하는 전략이다. 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다. 예를 들어 AUTO_INCREMENT 기능

IDENTITY 전략은 위처럼 DB에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 대 사용한다. 개발자가 엔티티에 직접 식별자를 할당하면 @Id 어노테이션만 있으면 되지만 지금처럼 식별자가 생성되는 경우는 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 한다. 이 전략을 사용하려면 @GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 지정하면 된다.

IDENTITY 매핑 코드

 

IDENTITY 사용 코드

*엔티티가 영속 상태가 되려면 식별자가 반드시 필요하지만 IDENTITY 식별자 생성 전략은 엔티티를 DB에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 DB에 전달된다. 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

 

SEQUENCE 전략

DB 시퀀스는 유일한 값을 순서대로 생성하는 특별한 DB오브젝트다. 이 전략은 시퀀스를 사용해서 기본 키를 생성한다. (오라클, PostgreSQL, DB2, H2 DB에서 사용할 수 있다.)

시퀀스 DDL
시퀀스 매핑 코드

 

우선 사용할 DB 시퀀스를 매핑 (@SequenceGenerator를 사용하여 BOARD_SEQ_GENERATOR 시퀀스 생성기 등록). 그리고 sequenceName 속성의 이름으로 BOARD_SEQ를 지정. 그 다음으로 그다음 전략을 SEQUENCE로 설정하고, generator = "BOARD_SEQ_GENERATOR"로 생성기를 선택한다. 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 시퀀스 생성기가 할당한다.

시퀀스 사용 코드

 

SEQUENCE vs IDENTITY

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

IDENTITY 전략은 먼저 엔티티를 DB에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당한다.

 

* @SequenceGenerator

속성 기능 기본값
name 식별자 생성기 이름 필수
sequenceName DB 시퀀스 이름 hibernate _ sequence
initialValue DDL 생성 시에만 사용 됨. DDL 생성할때 처음 시작하는 수를 정한다. 1
allocationSize 시퀀스 한 번 호출에 증가하는 수 50
catalog.schema DB catalog, schema이름  

매핑할 DDL

여기서 시퀀스를 호출할 때마다 값이 50씩 증가한다(기본값). DB 시퀀스 값이 하나식 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다. *더 자세히는 138P 참조

 

TABLE 전략

키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 DB 시퀀스를 흉내내는 전략. 이 전략은 테이블을 사용하므로 모든 DB에 적용할 수 있다.

TABLE 전략을 사용하려면 키 생성 용도 테이블을 만들어야 한다.

TABLE 전략 매핑 코드

@TableGenerator : 테이블 키 생성기를 등록

 

 

AUTO 전략

오라클을 선택하면 SEQUENCE를 MySQL을 선택하면 IDENTITY를 사용한다.

이렇게 해도 결과는 같다. AUTO 전략 장점은 DB를 변경해도 코드를 수정할 필요가 없다는 것이다.

 

 

기본 키 매핑 정리

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

 

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

간단히만 훑어보고 필요한 매핑을 사용할 일이 있을 때 찾아서 자세히 읽어보자.

 

@Column

@Column은 객체 필드를 테이블 컬럼에 매핑한다. 가장 많이 사용되고 기능도 많다.

속성 기능 기본값
name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름
insertable(거의 사용 x) 엔티티 저장시 이필드도 같이 저장한다. false로 설정하면 이 필드는 DB에 저장하지 않는다. true
updatable(거의 사용 x) 엔티티 수정 시 이 필드도 같이 수정한다. false로 설정하면 DB에 수정하지 않는다. true
table(거의 사용 x ) 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다. 현재 클래스가 매핑된 테이블
nullable(DDL) null 값의 허용 여부를 설정. false 로 설정하면 DDL 생성 시에 not null 제약 조건이 붙는다. true
unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 떄 사용한다.  
  DB 컬럼 정보를 직접 줄 수 있다. 필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다.
lenth(DDL) 문자 길이 제약조건, String 타입에만 사용 255
precesion, scale(DDL) BigDecimal 타입에서 사용한다. scale은 소수의 자릿수이다 precision = 19, scale = 2
  • nullable(DDL 생성 기능)

  • unique(DDL 생성 기능)

  • columnDefinition(DDL 생성 기능)

  • length(DDL 생성 기능)

  • precision, scale(DDL 생성 기능)

 

 

@Enumerated

속성 기능 기본값
value EnumType.ORDINAL : enum 순서를 DB에 저장
EnumType.STRING : enum 이름을 DB에 저장
EnumType.ORDINAL

enum 클래스

이것 보다는 밑에 있는게 편리함.

@Temporal

속성 기능 기본 값
value TemporalType.DATE : 날짜, DB에
TemporalType.TIME : 시간, DB time 타입과 매핑
TemporalType.TIMESTAMP : 날짜와 시간, DB timestamp 타입과 매핑
TemporalType은 필수적으로 지정해야 한다.

Temporal 사용 예시

DB에는 date(날짜), time(시간), timestamp(날짜와 시간) 세 가지 타입이 별도로 존재한다. @Temporal을 생략하면 Date 타입과 가장 유사한 timestamp로 정의

  • datetime : MySQL
  • timestamp : H2, 오라클, PostgreSQL

 

@Lob

DB BLOB, CLOB 타입과 매핑한다. @Lob에는 지정할 수 있는 속성이 없고 매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB으로 매핑

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

이렇게 사용하면 생성된 DDL은

  • 오라클 : lobString clob, lobByte blob
  • MySQL : lobString longtext, lobByte longblob
  • PostgreSQL : lobString text, lobByte oid

@Transient

이 어노테이션은 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.

 

@Access

  • 필드 접근 : AccessType.FIELD로 지정(권한이 private이어도 접근가능) 필드에 직접 접근한다.
  • 프로퍼티 접근 : AccessType.PROPERTY로 지정 (Getter를 사용한다) 접근자를 사용한다.

@Access를 설정하지 않으면 @Id 위치를 기준으로 설정된다.

필드 접근

@Id가 필드에 있으므로 @Access(AccessType.FIELD로 설정한 것과 같다. 따라서 @Access는 생략 가능

프로퍼티 접근 코드

마찬가지로 @Access 생략 가능하다.

필드접근+프로퍼티 접근 코드

위 코드는 기본은 필드 접근 방식을 사용하고 getFullName()만 프로퍼티 접근 방식을 사용한다. 따라서 회원 엔티티를 저장하면 회원 테이블의 FULLNAME 컬럼에 firstName + lastName의 결과가 저장된다.

'Spring > JPA' 카테고리의 다른 글

다양한 연관관계 매핑  (0) 2023.07.13
연관관계매핑 기초  (0) 2023.07.13
영속성 관리  (0) 2023.07.10
JPA 시작  (0) 2023.07.09
JPA란 무엇인가?  (0) 2023.07.09
profile

응애개발자

@Eungae-D

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!