[JPA] 엔티티 매핑

2025. 3. 29. 23:34·JPA
  • @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  
unique 유니크 제약조건   제약이름 알아보기 힘들어 잘 안씀
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.ORDINAL: enum 순서를 데이터베이스에 저장 사용x
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
'JPA' 카테고리의 다른 글
  • [JPA] 다양한 연관관계 매핑
  • [JPA] 연관관계 매핑
  • [JPA] 영속성 관리 - 내부 동작 방식
  • [JPA] Hello JPA
Naah
Naah
  • Naah
    blueprint
    Naah
  • 전체
    오늘
    어제
    • 분류 전체보기 (106)
      • Java (28)
      • Kotlin (0)
      • TypeScript (7)
      • React (22)
      • Next.js (1)
      • Spring (22)
      • JPA (12)
      • Spring Data JPA (6)
      • Querydsl (1)
      • Error (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
    • manage
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Naah
[JPA] 엔티티 매핑
상단으로

티스토리툴바