- @Entity, @Table : 객체와 테이블 매핑
- @Column : 필드와 컬럼 매핑
- @Id : pk 매핑
- @ManyToOne,@JoinColumn : 연관관계 매핑
객체와 테이블 매핑(@Entity, @Table)
@Entity
@Entity가 붙은 클래스 ← JPA가 관리하는 엔티티
JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
✓ 규정
- 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스 사용 x → @Entity를 붙일 수 없음
- 저장할 필드에 final 사용 x
✓ @Entity 속성
(안봐도됨)
속성 : name
@Entity(name="Member")//엔티티 이름 지정 (기본값: 클래스 이름) //가급적 기본값 사용하기 때문에 안해도됨
public class Member{
protected Member() {}
@Table
엔티티와 매핑할 테이블 지정
@Entity
@Table(name = "USER") //테이블명 다를 경우
public class Member {
| 속성 | 기능 | 기본값 |
| name | 매핑할 테이블 이름 | 엔티티 이름 사용 |
| catalog | 데이터베이스 catalog 매핑 | |
| schema | 데이터베이스 schema 매핑 | |
| uniqueConstraints (DDL) | DDL 생성 시에 유니크 제약 조건 생성 |
데이터베이스 스키마 자동 생성
- DDL을 애플리케이션 실행 시점에 자동 생성
- 테이블중심->객체중심
- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
- 이렇게 생성된 DDL은 개발 장비에서만 사용
- 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬 은 후 사용
✓ 속성
hibernate.hbm2ddl.auto
-resources/META-INF/persistence.xml
<property name="hibernate.hbm2ddl.auto" value="create" /> //앱실행시 기본테이블 모두 삭제 후 새로 생성
|
create
|
앱 실행 시, 기존테이블 삭제 후 다시 생성 (DROP + CREATE) |
|
create-drop
|
create와 같으나 종료시점에 테이블 DROP (DROP - CREATE - DROP)
|
|
update
|
변경분만 반영(운영DB에는 사용하면 안됨)
|
|
validate
|
엔티티와 테이블이 정상 매핑되었는지만 확인 (안됐을 시 오류 발생)
|
|
none
|
사용 안함 |
✓ 주의
- 운영 장비에는 절대 create, create-drop, update 사용하면 안됨!
- 개발 초기 단계는 create 또는 update
- 테스트 서버는 update 또는 validate
- 스테이징과 운영 서버는 validate 또는 none
근데 테스트 서버든 개발서버든, 가급적 쓰지말자...
✓ DDL 생성 기능
- 제약조건 추가
ex. 회원 이름 필수(NOT NULL), 10자 이하
@Column(nullable = false, length = 10)
private String name;
- 유니크 제약조건 추가
@Table(uniqueConstraints = {@UniqueConstraint( name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})
- DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않음
필드와 컬럼 매핑(@Column...)
예시
요구사항
(1) 회원 일반 회원, 관리자로 구분
(2) 회원 가입일, 수정일 有
(3) 회원 설명 필드 有 (길이 제한x)
public enum RoleType {
USER, ADMIN
}
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING) //DB엔 EnumType없는데 @Enumerated쓰면됨
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP) //날짜 타입 DATE,TIME,TIMESTAMP
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob //varcher넘어선 큰 컨텐츠 넣을 땐
private String description;
}
결과
create table Member (
id bigint not null,
age integer,
createdDate timestamp(6),
lastModifiedDate timestamp(6),
name varchar(255),
roleType varchar(255) check (roleType in ('USER','ADMIN')),
description clob,
primary key (id)
)
매핑 애노테이션 정리
| 어노테이션 | 설명 |
| @Column | 컬럼 매핑 |
| @Enumerated | enum 타입 매핑 |
| @Temporal | 날짜 타입 |
| @Lob | CLOB,BLOB |
| @Transient | 매핑 무시 |
✓ @Column
| 속성 | 설명 | 기본값 | 주석 |
| name | 컬럼명 | 객체 이름 | |
| insertable, updatable | 등록, 변경 가능 여부 | True | |
| (DDL) | |||
| nullable | not null | True | |
| 제약이름 알아보기 힘들어 잘 안씀 | |||
| length | 길이 제약 | (255) | String타입만 |
| columnDefinition | 컬럼 정의 직접 입력 | ex) cD= "varchar(100) default ‘EMPTY'" | |
| precision scale |
소수점을 포함한 전체 자릿수 소수의 자릿수 |
precision=19, scale=2 | BigDecimal, BigInteger타입에 사용 아주 큰 숫자나 정밀한 소수를 다룰 때 |
@Column(updatable = false, nullable = false, length = 10)
@Column(precision=19, scale=2)
private BigDecimal num;
✓ @Enumerated
| 속성 | 설명 | 기본값 |
| value | EnumType.STRING: enum 이름을 데이터베이스에 저장 필수로 적기 |
EnumType.ORDINAL |
참고. 왜 사용x? 중간에 값이 추가되면 기존 데이터 의미가 바뀌는 문제가 발생
예: USER, ADMIN → GUEST, USER, ADMIN 으로 변경 시 USER(0)이 GUEST(0)으로 잘못 해석될 수 있음
✓ @Temporal
날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용 (예전)
| 속성 | 설명 |
| value | TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑(예: 2013–10–11) TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑(예: 11:11:11) TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이 스 timestamp 타입과 매핑(예: 2013–10–11 11:11:11) |
LocalDate, LocalDateTime을 사용할 때는 생략 가능(최신 하이버네이트 지원) 지금은 이거쓰면됨
private LocalDate birthDate; // 날짜 (yyyy-MM-dd)
private LocalDateTime createdDateTime; // 날짜 + 시간 (yyyy-MM-ddTHH:mm:ss)
✓ @Lob
- 매핑하는 필드 타입 문자면 CLOB 매핑, 나머지 BLOB 매핑
- CLOB: String, char[], java.sql.CLOB
- BLOB: byte[], java.sql. BLOB
기본 키 매핑(@Id,@GeneratedValue)
@Id : 직접 할당
@GeneratedValue : 자동 생성
✓ @GeneratedValue
@GeneratedValue(strategy = GenerationType.AUTO)
- IDENTITY : 데이터베이스에게 위임 (MYSQL의 AUTO_ INCREMENT)
- SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용 (ORACLE) - @SequenceGenerator 필요
- TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용 - @TableGenerator 필요
- AUTO : 방언에 따라 자동 지정 (기본값)
1. IDENTITY
테이블별 동작
IDENTITY 전략은 PK를 DB에서 생성하므로, 넣기 전에는 PK 값을 알 수 없음
따라서 이 전략만 예외적으로, persist() 시점에 즉시 INSERT 쿼리를 실행해 DB에서 생성된 키를 받아오고, 1차 캐시에 등록함
∴ INSERT 쿼리 실행 → 생성된 키 받아옴 → 1차 캐시 등록
2. SEQUENCE, @SequenceGenerator
@SequenceGenerator : 테이블마다 시퀀스 따로 관리하고 싶을때 사용
SEQUENCE : JPA가 DB 시퀀스에서 다음 값 조회 → 조회한 키를 엔티티에 할당 → INSERT 쿼리 실행 → 1차 캐시 등록
인데 매번 다음값 조회하긴 보단 성능 최적화위해 allocationSize활용
@Entity
@SequenceGenerator(
name = “MEMBER_SEQ_GENERATOR", // 자바 시퀀스 제너레이터 이름 (필수)
sequenceName = “MEMBER_SEQ", //DB 시퀀스 이름
initialValue = 1, //시작값 (기본:1)
allocationSize = 1) //미리 확보 개수❗️(50) 기본값 50 이니 1씩할거면 1로
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE, //전략 선택
generator = "MEMBER_SEQ_GENERATOR") //제너레이터 이름
private Long id;
em.persist(member1); // seq = 1 (call next value 2번 호출됨 → 1, 51 확보)
em.persist(member2); // seq = 2 (메모리에서 사용)
em.persist(member3); // seq = 3
...
em.persist(member50); // seq = 50
em.persist(member51); // 이때 다시 DB 시퀀스 호출 → 51, 101 확보
3. @TableGenerator
(잘안씀 가볍게 보기)
시퀀스를 지원하지 않는 DB에서 테이블을 이용해 기본 키 값을 생성할 때 사용
모든 데이터베이스에 적용이 가능하지만, 성능이 떨어짐
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
@Entity
@TableGenerator(
name = "MEMBER_SEQ_GENERATOR", // JPA에서 사용할 생성기 이름(필수)
table = "MY_SEQUENCES", // 키 생성용 테이블명 (기본:hibernate_sequences)
pkColumnValue = "MEMBER_SEQ", // pk 컬럼명
allocationSize = 1 // 증가 수(50)
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_GENERATOR")
private Long id;
private String name;
}
속성 자세히
| 속성 | 설명 | 기본값 |
| name | 식별자 생성기 이름 | 필수 |
| table | 키생성 테이블명 | hibernate_sequences |
| pkColumnName | 시퀀스 컬럼명 | sequence_name |
| valueColumnName | 시퀀스 값 컬럼명 | next_val |
| pkColumnValue | 키로 사용할 값 이름 엔티티 이름 | |
| initialValue | 초기 값, 마지막으로 생성된 값이 기준이다 | 0 |
| allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) | 50 |
| catalog, schema | 데이터베이스 catalog, schema 이름 | |
| uniqueConstraint s(DDL) | 유니크 제약 조건을 지정할 수 있다. |
🌿권장하는 식별자 전략
pk 제약 조건 : null 아님(NOT NULL), 유일(UNIQUE), 변하면 안된다.미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.예를 들어 주민등록번호도 기본 키로 적절하지 않다.권장 : Long형 + 대체키(시퀀스,UUID) + 키 생성전략 사용
그냥 AUTO_ INCREMENT, Sequence-object써라...
예제 - 1. 요구사항 분석과 기본 매핑
요구사항
- 회원 상품 주문 가능
- 주문 시 여러 종류 상품 선택 가능
기능
- 회원기능 - 등록, 목록
- 상품기능 - 등록, 목록, 수정
- 주문기능 - 주문, 내역, 취소
도메인 모델 분석

- 회원 - 주문 : 회원은 여러 번 주문 가능 → 일대다 관계
- 주문 - 상품 : 주문시 여러 상품 선택가능, 같은 상품도 여러 번 주문가능 → 다대다 관계
But, 다대다 관계는 관계형 데이터베이스는 물론 엔티티에서도 거의 사용하지 않음!
➡︎ 주문상품이라는 연결 엔티티 추가해 다대다 관계를 일대다, 다대일 관계로 풀어냄.
그리고 주문상품에는 해당 상품을 구매한 금액과 수량 정보가 포함됨.
테이블 설계

- 회원(MEMBER) : 이름(NAME), 주소 정보(CITY, STREET, ZIPCODE) 가짐
- 주문(ORDERS) : 상품을 주문한 회원(MEMBER_ID)을 외래 키로 가진다. 그리고 주문날짜(ORDERDATE)와 주문 상태(STATUS)를 가진다. 주문 상태는 주문(ORDER)과 취소(CANCEL)를 표현할 수 있다.
- 주문상품(ORDER_ITEM) : 주문(ORDER_ID)과 주문한 상품(ITEM_ID)을 외래 키로 가진다. 주문 금액(ORDERPRICE), 주문 수량(COUNT) 정보를 가진다.
- 상품(ITEM) : 이름(NAME), 가격(PRICE), 재고수량(STOCKQUANTITY)을 가진다. 상품을 주문하면 재고수량이 줄어든다.
엔티티 설계와 매핑

Member
-java.jpabook.jpashop.domain.Member.java
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String city;
private String street;
private String zipcode;
//Getter, Setter..
}
나머지
-Order
@Entity
@Table(name = "ORDERS") //ooder예약어 걸린경우 있어 orders로 많이 씀
public class Order {
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@Column(name = "MEMBER_ID")
private Long memberId;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
-OrderStatus
public enum OrderStatus {
ORDER, CANCEL
}
OrderItem
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
@Id
@GeneratedValue
@Column(name="ORDER_ITEM_ID")
private Long id;
@Column(name = "ORDER_ID")
private Long orderId;
@Column(name = "ITEM_ID")
private Long itemId;
private int orderPrice; //주문 가격
private int count; //주문 수량
Item
@Entity
public class Item {
@Id @GeneratedValue
@Column(name="ITEM_ID")
private Long id;
private String name;
private int price; //가격
private int stockQuantity; //재고 수량
현재 설계 문제점
현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
테이블의 외래키를 객체에 그대로 가져옴
객체 그래프 탐색이 불가능
참조가 없으므로 UML도 잘못됨
'JPA' 카테고리의 다른 글
| [JPA] 다양한 연관관계 매핑 (0) | 2025.04.03 |
|---|---|
| [JPA] 연관관계 매핑 (0) | 2025.03.31 |
| [JPA] 영속성 관리 - 내부 동작 방식 (0) | 2025.03.26 |
| [JPA] Hello JPA (0) | 2025.03.24 |
| [JPA] 개요 (0) | 2025.03.19 |