JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다.
다음은 대표 어노테이션들이다.
- 객체와 테이블 매핑: @Entity, @Table
- 기본 키 매핑: @Id
- 필드와 컬럼 매핑: @Column
- 연관관계 매핑: @ManyToOne, @JoinColumn
@Entity
@Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라 부른다.
- 기본 생성자는 필수다. (파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
- 저장할 필드에 final을 사용하면 안 된다.
속성 | 기능 | 기본값 |
name | JPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값을 사용한다. 다른 패키지에 이름이 같은 엔티티 클래스가 있으면 이름을 지정해서 충돌하지 않도록 해야 한다. | 클래스 이름을 그대로 사용한다. |
@Table
@Table은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
속성 | 기능 | 기본값 |
name | 매핑할 테이블 이름 | 엔티티 이름을 사용한다. |
catalog | catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다. | |
schema | schema 기능이 있는 데이터베이스에서 schema를 매핑한다. | |
uniqueConstraints(DDL) | DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 떄만 사용된다. |
다양한 매핑 사용
- @Enumerated: 자바의 enum을 사용할 떄 매핑
- @Temporal: 자바의 날짜 타입
DDL 생성 기능
@Entity(name="Member")
@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;
}
- NAME 컬럼에 not null 제약조건을 추가하고 varchar(10)으로 문자의 크기가 10자리로 제한되었다.
- 유니크 제약조건이 추가된다.
- ALTER TABLE MEMBER ADD CONSTRAINT NAME_AGE_UNIQUE UNIQUE (NAME, AGE)
이런 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다. 직접 DDL을 만든다면 사용할 이유가 없다. 그래도 이 기능을 사용하면 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다.
기본 키 매핑
- 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
- 자동 생성: 대리 키 사용 방식
- IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
- SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
- TABLE: 키 생성 테이블을 사용한다.
- 자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
- 오라클은 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.
기본 키 직접 할당 전략
- @Id 적용 가능 자바 타입
- 자바 기본형, 자바 래퍼형, String
- java.util.Date, java.sql.Date, java.math.BigDecimal, java.math.BigInteger
IDENTITY 전략
- 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다.
- 예) MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
- MySQL, PostgreSQL, SQL Server, DB2 에서 사용한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
- JDBC3에 추가된 Statement.getGeneratedKeys()를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어 올 수 있다.
- IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
SEQUENCE 전략
- 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영솟ㄱ성 컨텍스트에 저장한다.
- 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다.
- 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
- @SequenceGenerator는 @GeneratedValue 옆에 사용해도 된다.
@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;
}
속성 | 기능 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 |
TABLE 전략
- 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
- TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 말ㄴ들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.
- TABLE 전략을 사요앟려면 키 생성 용도로 사용할 테이블을 만들어야 한다.
AUTO 전략
- 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
- 오라클: SEQUENCE, MySQL: IDENTITY
- 데이터베이스를 변경해도 코드를 수정하 필요가 없다
권장하는 식별자 선택 전략
- 데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.
- null 값은 허용하지 않는다.
- 유일해야 한다.
- 변해선 안된다.
- 전략 1. 자연 키
- 비즈니스에 의미가 있는 키 (주민번호, 이메일, 전화번호)
- 전략 2. 대리 키
- 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다. (오라클 시퀀스, auto_increment, 키생성 테이블 사용)
- JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.
필드와 컬럼 매핑: 레퍼런스
@Column
- 객체 필드를 테이블 컬럼에 매핑한다. name, nullable이 주로 사용된다.
- @Column 생략:
- int 같은 자바 기본 타입에는 null 값을 입력할 수 없고 Integer 같은 객체 타입일 때만 null 값이 허용된다.
- 따라서 기본 타입을 DDL로 생성할 때는 not null 제약 조건을 추가하는 것이 안전하다.
속성 | 기능 | 기본값 |
name | 필드와 매핑할 테이블의 컬럼 이름 | 객체의 필드 이름 |
nullable(DDL) | null 값의 허용 여부를 설정한다. false -> not null | true |
unique(DDL) | @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 두 컬럼 이상 시 -> @Table.uniqueConstraints 사용 | |
columnDefinition(DDL) | 데이터베이스 컬럼 정보를 직접 줄 수 있다. | 필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다. |
lenegth(DDL) | 문자 길이 제약조건, String 타입에만 사용한다. | 255 |
precision, scale(DDL) | BigDecimal 타입에서 사용한다. precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다. | precision=19, scale=2 |
@Enumerated
- 자바의 enum 타입을 매핑할 때 사용한다.
- EnumType.STRING을 권장한다.
속성 | 기능 | 기본값 |
value | EnumType.ORDINAL: enum 순서를 데이터베이스에 저장 EnumType.STRING: enum 이름을 데이터베이스에 저장 | EnumType.ORDINAL |
@Temporal
- 날짜 타입을 매핑할 때 사용한다.
- 자바의 Date 타입에는 년월일 시분초가 있지만 데이터베이스에는 date, time, timestamp(날짜와 시간)라는 세 가지 타입이 존재한다.
- @Temporal을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의된다.
속성 | 기능 | 기본값 |
value | TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑(예: 2013-10-11) TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑(예: 11:11:11) TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이스 timestamp 타입과 매핑(예: 2013-10-11 11:11:11) | TemporalType은 필수로 지정해야 한다. |
@Lob
- 데이터베이스 BLOB, CLOB 타입과 매핑한다.
- 필드 타입이 문자면 CLOB, 나머지는 BLOB으로 매핑한다.
@Transient
- 이 필드는 매핑하지 않는다. 따라서 데이터베이스에 저장하지도 않고 조회하지도 않는다.
- 객체에 임시로 어떤 값을 보관하고 싶을 떄 사용한다.
@Access
- JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
- 필드 접근:
- AccessType.FIELD로 지정
- 필드에 직접 접근한다. 필드 접근 권한이 private이어도 접근할 수 있다.
- 프로퍼티 접근:
- AccessType.PROPERTY로 지정한다.
- getter를 사용한다.
'JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 고급 매핑 (0) | 2020.09.04 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 다양한 연관관계 매핑 (0) | 2020.09.04 |
[자바 ORM 표준 JPA 프로그래밍] 연관관계 매핑 기초 정리 (0) | 2020.03.12 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 관리 정리 (0) | 2020.03.10 |
[자바 ORM 표준 JPA 프로그래밍] JPA 소개 정리 (0) | 2020.03.09 |