학습목표
엔티티 매핑
- 엔티티 매핑을 실습합니다.
- 엔티티간의 연관관계 매핑을 실습합니다. (OneToOne, OneToMany, ManyToOne)
- 고급 매핑 전략에 대해 소개합니다.
프록시와 연관관계
- 프록시에 대해 학습합니다.
- 즉시 로딩(Eager fetch), 지연 로딩(Lazy fetch)에 대해 학습합니다.
- 영속성 전이에 대해 학습합니다. (CASCADE, 고아객체)

단일 엔티티 매핑
JPA는 엔티티 객체를 생성할 때, 기본 생성자(Default Constructor)를 사용한다.
@Entity
@Table
@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 옵션
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
기본키 매핑전략
@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; }
데이터 중심 설계의 문제점

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)
@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; }
@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; }
@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; }
@Entity @Table(name = "item") @Getter @Setter public class Item { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private int price; private int stockQuantity; }

설계한 엔티티로 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()); }