[JPA 기본편] 4. 엔티티 매핑
객체와 테이블 매핑
@Entity
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity가 필수이며 @Entity가 붙은 클래스는 JPA가 관리하며, 엔티티라 한다.
- 기본 생성자가 필수로 있어야 함
- final 클래스, enum, interface, inner 클래스에 사용할 수 없으며, 저장할 필드에 final을 사용하면 안된다.
- @Table(name=" ")를 사용하여 테이블 이름을 정할 수 있다.
데이터베이스 스키마 자동 생성
- DDL을 애플리케이션 실행 시점에 자동으로 생성하며 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성한다.
- 데이터베이스 방언별로 DDL이 달라짐 → Oracle로 실행하면 varchar2로 생성되지만 h2로 실행하면 varchar로 생성된다.
- 테이블 중심 → 객체 중심
- 스테이징, 운영 서버 : validate, none
- 개발 초기 : create, update
- 테스트 서버(여러 명이 개발 중인 중간 서버) : update, validate
- persistence.xml의 hibernate.hbm2ddl.auto의 value값 또는 application.yml의 spring:jpa:hibernate:ddl-auto의 값을 조정
옵션 | 설명 |
create | 기존 테이블 삭제 후 다시 생성 (DROP + CREATE) |
create-drop | create와 같으나 종료 시점에 테이블 DROP |
update | 변경분만 반영 (운영DB에서는 사용하면 안됨) |
validate | 엔티티와 테이블이 정상 매핑되었는지만 확인 |
none | 사용하지 않음 |
DDL 생성 기능
@Column(unique = true, length = 10) 으로 제약 조건을 추가할 수 있으며, DDL 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향을 주지 않는다. 즉, DB에만 영향을 준다.
반면, @Table(name = " ")은 insert 쿼리에 영향을 준다.
필드와 컬럼 매핑
@Entity
@Table(name = "MBR")
public class Member {
@Id
private Long id;
@Column(name = "name") // DB에 표시되는 이름 → @Column : 컬럼 매핑
private String username;
private Integer age; // Integer로 해도 알아서 디비 타입과 매핑
@Enumerated(EnumType.STRING) // @Enumberated : ENUM타입 매핑 시
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP) //DB에 날짜관련 매핑 시
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP) //DB에 날짜관련 매핑 시 → @Temporal : 날짜 타입 매핑
private Date lastModifiedDate;
@Lob //varchar보다 큰 타입을 넣고 싶은 경우 → @LOB : BLOB, CLOB 매핑
private String description;
@Transient // @Transient : 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
private int temp; // DB에 넣지 않는 데이터
}
@Column
속성 | 설명 | 기본값 |
name | 필드와 매핑할 테이블의 컬럼 이름 | 객체의 필드 이름 |
insertable, updatable | 등록, 변경 가능 여부. insert문이나 update문이 나갈 때, 이 컬럼의 변경 여부를 결정하는 것으로 DB에서 강제로 업데이트하지 않는한, updateable = false로 지정하면 절대로 반영되지 않는다. |
TRUE |
nullable(DDL) | null 값의 허용 여부를 설정. false로 설정하면 DDL 생성 시에 NOT NULL 제약조건이 붙는다. | |
unique(DDL) | @Table의 uniqueConstraints와 같지만 한 컬럼에서 간단히 유니크 제약조건을 걸 때 사용. 한 컬럼에만 가능하고 이름이 알아보기 어려운 랜덤값이 되므로 잘 사용하지 않는다. |
|
columnDefinition(DDL) | 데이터베이스 컬럼 정보를 직접 줄 수 있다. ex) varchar(100) default 'EMPTY' |
필드의 자바 타입과 방언 정보를 사용함 |
length(DDL) | 문자 길이의 제약 조건으로 String 타입에만 사용 | 255 |
precision, scale(DDL) | BigDecimal 타입에서 사용(BigInteger도 사용 가능) 아주 큰 숫자나 소수점 사용 시에 사용 |
precision=19, scale=2 |
@Enumerated
속성 | 설명 | 기본값 |
value | - EnumType.ORDINAL : enum 순서를 데이터베이스에 저장 - EnumType.STRING : enum 이름을 데이터베이스에 저장 |
EnumType.ORDINAL |
- Enum 타입을 사용할 때는 STRING으로 저장해야 한다.
- ORDINAL으로 생성 시, Integer 타입으로 생성된다. Enum 타입이 정수형으로 들어가게 되는데 항목이 변경, 추가되면 숫자가 꼬이게 되므로 절대로 ORDINAL을 사용하면 안된다!!
@Temporal
- 날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용하며 과거 버전에서 사용했던 방식이다.
- 최신 버전에서는 LocalDate, LocalDateTime을 주로 사용하며, 이걸 사용할 때는 @Temporal을 생략해도 된다.
@Lob
- @Lob은 지정할 수 있는 속성이 없으며 매핑하는 필드 타입이 문자면 CLOB 매핑이 되고 나머지는 BLOB 매핑이 된다.
@Transient
- 매핑을 하지 않을 때 사용하며, 데이터베이스에 저장과 조회를 하지 않는다.
- 주로 메모리 상에서만 임시로 어떤 값을 보관하고 싶을 때 사용한다.
기본 키 매핑
- 직접 할당 : @Id만 사용
- 자동 생성 : @Id와 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택

권장하는 식별자 전략
- 기본 키 제약 조건 : null 아님, 유일, 변하면 안됨
- id는 Long형을 사용하는게 좋음. Integer(10억 제한)를 사용했다가 나중에 타입을 바꾸는 것보다 Long형을 처음부터 사용하는게 더 나음
- 대체키를 사용해라. → 미래까지 기본 키 제약 조건을 만족하는 자연키를 찾기 어려우며 예를 들어 주민번호를 PK로 쓴다면, 다른 테이블의 FK도 다 주민번호로 설정해야 한다. 이러면 테이블마다 주민번호를 다 넣어주고 바꿔야되기 때문에 DB 마이그레이션할 때 불편하므로 비즈니스 관련 값을 사용하지 말고 랜덤 키값인 Long을 사용하는 것을 권장한다.
권장 : Long형 + 대체키 + 키 생성 전략 사용
기본 키 자동 생성 전략 - 4가지
IDENTITY 전략
기본 키 생성을 DB에 위임하는 전략으로 MySQL(AUTO_INCREMENT), PostgreSQL, SQL Server DB2에서 사용한다.
JPA는 보통 트랜잭션커밋 시점에 INSERT SQL을 실행하는데 이 전략은 DB에 먼저 INSERT SQL을 실행한 후에 기본 키를 알 수 있어서 em.persist() 시점에 즉시 INSERT SQL을 실행하고 DB에서 식별자를 조회하게 된다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
위 코드와 같이 작성하여 사용하면 되며 아래의 결과를 보면 em.persist() 시점에 INSERT SQL이 실행된 것을 확인할 수 있다.

이 전략을 사용하면 Id 값이 자동으로 채워진다.
SEQUENCE 전략
유일한 값을 순서대로 생성하는 특별한 데이터베이스 오블젝트이다.
여기에서 만든 시퀸스를 사용해서 기본키를 생성하며, Oracle, PostgreSQL, DB2, H2에서 시퀸드를 지원한다.

@SequenceGenerator를 사용해서 "MEMBER_SEQ_GENERATOR"라는 시퀸스 생성기를 등록했다.
그리고 sequenceName으로 "MEMBER_SEQ"로 지정했는데 이러면 JPA가 이 시퀸스 생성기를 DB의 "MEMBER_SEQ"와 매핑한다.
@GeneratedValue를 사용해서 SEQUENCE 자동 생성 전략으로 타입을 정하고, 위에서 등록한 "MEMBER_SEQ_GENERATOR"라는 시퀸스 생성기를 선택했다. 이러면 값이 추가될 때마다 id 식별자 값이 저 시퀸스 생성기에 하나씩 증가된다.

SEQUENCE 전략을 사용했을 때의 결과를 보면 IDENTITY 전략과는 다르게 persist() 시점에 시퀸스를 통해서 식별자를 조회하는 추가 작업(왼쪽 3번)이 필요하다. 시퀸스 전략은 persist()에 모아놨다가 commit()에 한 번에 보내는 것이 가능하다. 따라서 DB와 2번 통신하게 된다.
DB 시퀸스 생성 부분에 "create sequence MEMBER_SEQ start with 1 increment by 1" 이라는게 있는데 이건 1부터 시작해서 1씩 늘리겠다는 의미이다. 이렇게 사용하면 매번 통신할 때마다 DB에 두 번씩 접근하므로 비효율적이다. JPA는 시퀸스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator의 allocationSize를 사용한다. 기본값은 50이며 일반적으로는 50~100으로 설정한다고 하지만 성능이 중요하지 않으면 1로 설정해도 된다.(일단 스킵...)
TABLE 전략
키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀸스를 흉내내는 전략이다.
모든 데이터베이스에 적용이 가능하지만 성능이 나쁘다.

@TableGenerator로 테이블 키 생성기를 등록하고 생성한 MY_SEQUNCES 테이블을 키 생성용 테이블로 매핑했다. 그리고 @GeneratedValue의 generate 속성으로 "MEMBER_SEQ_GENERATOR" 테이블 키 생성기를 지정했다. 이러면 id 식별자 값은 "MEMBER_SEQ_GENERATOR" 테이블 키 생성기가 할당한다.

TABLE 전략은 값을 조회하면서 SELECT 사용 후 다음 값을 증가시키기 위해 UPDATE를 한 번 더 사용한다. 이는 시퀸스 전략보다 한 번 더 DB와 통신한다는 차이점이 있다.
[출처]
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard → 이 글은 김영한님의 JPA 강의 중 4장을 듣고 정리한 내용입니다