HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
[KDT] SpringBoot Part4/강의자료/
SpringBoot Part4 4-1. 엔티티매핑

SpringBoot Part4 4-1. 엔티티매핑

학습목표

엔티티 매핑
  • 엔티티 매핑을 실습합니다.
  • 엔티티간의 연관관계 매핑을 실습합니다. (OneToOne, OneToMany, ManyToOne)
  • 고급 매핑 전략에 대해 소개합니다.
프록시와 연관관계
  • 프록시에 대해 학습합니다.
  • 즉시 로딩(Eager fetch), 지연 로딩(Lazy fetch)에 대해 학습합니다.
  • 영속성 전이에 대해 학습합니다. (CASCADE, 고아객체)
주문 도메인 을 구성하는 단일 엔티티를 만들고, 이들의 연관관계를 설정한다.
주문 도메인 을 구성하는 단일 엔티티를 만들고, 이들의 연관관계를 설정한다.

단일 엔티티 매핑

JPA는 엔티티 객체를 생성할 때, 기본 생성자(Default Constructor)를 사용한다.
@Entity
속성
기능
기본값
name
JPA에서 사용할 엔티티 이름을 지정한다.
클래스 이름
@Table
속성
기능
기본값
name
매핑할 테이블 이름
엔티티 이름
@Entity @Table(name = "member") public class Member { @Id private Long id; private String name; private String nickName; private int age; private String address; private String description; // getter, setter }
 

데이터 베이스 스키마 자동생성

spring: h2: console: enabled: true jpa: generate-ddl: true database: H2 show-sql: true open-in-view: false properties: hibernate: dialect: org.hibernate.dialect.H2Dialect query.in_clause_parameter_padding: true hbm2ddl: auto: create-drop
AUTO DDL 옵션
옵션
설명
create
기존 테이블을 삭제하고 새로 테이블을 생성한다. (DROP + CREATE)
create-drop
어플리케이션 종료시 생성한 DDL 을 제거한다. (DROP + CRATE + DROP)
update
테이블, 엔티티 매핑정보를 비교하여 변경사항을 수정한다.
validate
테이블, 엔티티 매핑정보를 비교해서 차이가 있으면 경도를 남겨 어플리케이션을 실행하지 않는다.
none
자동 생성 기능을 사용하지 않는다.

DDL 옵션

@Entity @Table(name = "member") @Getter @Setter public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; @Column(name = "name", nullable = false, length = 30) private String name; @Column(nullable = false, length = 30, unique = true) private String nickName; // nick_name private int age; // age @Column(name = "addres", nullable = false) private String address; @Column(name = "description", nullable = true) private String description; }
drop table if exists member CASCADE drop sequence if exists hibernate_sequence create sequence hibernate_sequence start with 1 increment by 1 create table member (id bigint not null, address varchar(255) not null, age integer not null, description varchar(255), name varchar(30) not null, nickName varchar(30) not null, primary key (id)) alter table member add constraint UK_1m3ighjll05v7njjxeopp823j unique (nickName)
@Column
속성
기능
기본값
name
필드와 매핑할 테이블의 컬럼이름
객체의 필드이름
insertable
엔티티 저장시 필드도 같이 저장한다. false 이면 읽기 전용으로 사용한다.
true
updateable
엔티티 수정시 필드도 같이 수정한다. false 이면 읽기 전용으로 사용한다.
true
nullable (DDL)
null 값 허용 여부를 설정한다. false 이면 not null 제약조건이 추가된다.
true
unique (DDL)
해당 컬럼의 유니크 제약조건을 추가한다.
length (DDL)
문자 길이 제약조건으로 사용된다 (String 타입에만 사용)
255

기본키 매핑전략

@Id

  • 직접 할당
    • 영속화 전에 애플리케이션에서 직접 값을 할당한다.
      • @Id @Column(name = "id") private Long id;
  • SEQUENCE
    • 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속화
    • ORACLE, H2
      • @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id;
  • TABLE
    • 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속화
  • IDENTITY
    • 데이터베이스 엔티티를 저장해서 식별자 값을 획득한 후 영속화
    • 엔티티가 영속화 되려면 식별자 값이 반드시 필요하기 때문에, em.persist() 시점에 INSERT 쿼리가 수행된다.
    • MySQL (AUTO_INCREMENT)
      • @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
  • AUTO
    • 데이터 베이스 방언(dialect)에 따라서 자동으로 전략을 선택
      • @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Id @GeneratedValue private Long id;
 

기타 컬럼매핑

@Entity @Table(name = "orders") public class Order { @Id @Column(name = "id") private String uuid; @Column(name = "order_datetime", columnDefinition = "TIMESTAMP") private LocalDateTime orderDatetime; @Enumerated(EnumType.STRING) private OrderStatus orderStatus; @Lob private String memo; }
 

데이터 중심 설계의 문제점

notion image
create sequence hibernate_sequence start with 1 increment by 1 create table item (id bigint not null, price integer not null, stockQuantity integer not null, primary key (id)) create table member (id bigint not null, address varchar(255) not null, age integer not null, description varchar(255), name varchar(30) not null, nickName varchar(30) not null, primary key (id)) create table order_item (id bigint not null, item_id bigint, order_id varchar(255), price integer not null, quantity integer not null, primary key (id)) create table orders (id varchar(255) not null, member_id bigint, memo clob, order_datetime TIMESTAMP, orderStatus varchar(255), primary key (id)) alter table member add constraint UK_1m3ighjll05v7njjxeopp823j unique (nickName)
JPA DDL 옵션을 활용해서, 설계한 ERD로 DDL 쿼리가 수행된다.
 
@Entity @Table(name = "member") @Getter @Setter public class Member { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; @Column(name = "name", nullable = false, length = 30) private String name; @Column(nullable = false, length = 30, unique = true) private String nickName; private int age; @Column(name = "address", nullable = false) private String address; @Column(name = "description", nullable = true) private String description; }
Member Entity
@Entity @Table(name = "orders") @Getter @Setter public class Order { @Id @Column(name = "id") private String uuid; @Column(name = "order_datetime", columnDefinition = "TIMESTAMP") private LocalDateTime orderDatetime; @Enumerated(EnumType.STRING) private OrderStatus orderStatus; @Lob private String memo; @Column(name = "member_id") // fk private Long memberId; }
Order Entity
@Entity @Table(name = "order_item") @Getter @Setter public class OrderItem { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private int price; private int quantity; @Column(name = "order_id") // fk private String order_id; @Column(name = "item_id") // fk private Long item_id; }
OrderItem Entity
@Entity @Table(name = "item") @Getter @Setter public class Item { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private int price; private int stockQuantity; }
Item Entity
notion image
설계한 엔티티로 DB 스키마를 설계하면 설계한, ERD 형태로 테이블이 생성되기는 하지만, 실제 엔티티 객체사이에는 서로 참조하지 않고 있다.
 
@Test void 잘못된_설계() { Member member = new Member(); member.setName("kanghonggu"); member.setAddress("서울시 동작구(만) 움직이면 쏜다."); member.setAge(33); member.setNickName("guppy.kang"); member.setDescription("백앤드 개발자에요."); EntityManager entityManager = emf.createEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); entityManager.persist(member); Member memberEntity = entityManager.find(Member.class, 1L); Order order = new Order(); order.setUuid(UUID.randomUUID().toString()); order.setOrderDatetime(LocalDateTime.now()); order.setOrderStatus(OPENED); order.setMemo("부재시 전화주세요."); order.setMemberId(memberEntity.getId()); // 외래키를 직접 지정 entityManager.persist(order); transaction.commit(); Order orderEntity = entityManager.find(Order.class, order.getUuid()); // FK 를 이용해 회원 다시 조회 Member orderMemberEntity = entityManager.find(Member.class, orderEntity.getMemberId()); // orderEntity.getMember() // 객체중심 설계라면 객체그래프 탐색을 해야하지 않을까? log.info("nick : {}", orderMemberEntity.getNickName()); }
데이터 중심 설계로 인한 잘못된 JPA 엔티티 객체 사용