아래 요구사항을 가지고 간단한 쇼핑몰 예제를 만들어 보자.
- 회원과 주문의 1대다 관계
- 주문과 주문상품의 관계 1대다
- 주문상품과 상품의 관계 다대일
테이블 설계와 엔티티 설계 매핑 그림은 아래와 같다.
엔티티 설계와 매핑 예시를 코드로 구현해보자.
maven으로 새로운 프로젝트를 생성해준다.
폴더구조에서 persistence.xml을 만들어 주고 설정을 해준다.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/jpashop"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.jdbc.batch_size" value="10"/>
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
Member Order OrderItem Item Class를 각각 만들어준다.
Member Class에서 Id 식별자는 필수로 해준다.
getter setter을 만들어 준다.(setter는 많이 만들면 코드 추적하기가 어렵고 유지보수성에서 떨어진다.)
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String city;
private String street;
private String zipcode;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}
@GeneratedValue도 보면 기본이 auto이다.
public @interface GeneratedValue {
/**
* (Optional) The primary key generation strategy
* that the persistence provider must use to
* generate the annotated entity primary key.
*/
GenerationType strategy() default AUTO;
/**
* (Optional) The name of the primary key generator
* to use as specified in the {@link SequenceGenerator}
* or {@link TableGenerator} annotation.
* <p> Defaults to the id generator supplied by persistence provider.
*/
String generator() default "";
}
Order Class
Enumerated해서 STRING를 넣어준다.
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.CascadeType.*;
import static javax.persistence.FetchType.*;
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
private Long memberId;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
}
OrderStatus는 Enum으로 만들어준다.
package jpabook.jpashop.domain;
public enum OrderStatus {
ORDER, CANCEL
}
Order의 테이블을 보면 ORDERS라고 되어있다. db에 orderby라고 예약어로 되어있어서
썼을 때 되는 db도 있고 안되는 db도 있어서 ORDERS라고 쓴다.
Item Class
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Item {
@Id
@GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
Order Class에도 Column을 각각 넣어준다.
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.CascadeType.*;
import static javax.persistence.FetchType.*;
@Entity
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;
}
OrderItem Class
package jpabook.jpashop.domain;
import javax.persistence.*;
import static javax.persistence.FetchType.LAZY;
@Entity
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;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public Long getItemId() {
return itemId;
}
public void setItemId(Long itemId) {
this.itemId = itemId;
}
public int getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(int orderPrice) {
this.orderPrice = orderPrice;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
Order class에서 getter setter 생성
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.CascadeType.*;
import static javax.persistence.FetchType.*;
@Entity
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;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(LocalDateTime orderDate) {
this.orderDate = orderDate;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
}
JpaMain을 만들어주고 아래와 같이 코드작성을 하고 빌드 돌리면
package jpabook.jpashop;
import jpabook.jpashop.domain.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
테이블들이 생성된다.
Hibernate:
drop table ORDERS if exists
Hibernate:
drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate:
create table Item (
ITEM_ID bigint not null,
name varchar(255),
price integer not null,
stockQuantity integer not null,
primary key (ITEM_ID)
)
Hibernate:
create table Member (
MEMBER_ID bigint not null,
city varchar(255),
name varchar(255),
street varchar(255),
zipcode varchar(255),
primary key (MEMBER_ID)
)
Hibernate:
create table OrderItem (
ORDER_ITEM_ID bigint not null,
count integer not null,
ITEM_ID bigint,
ORDER_ID bigint,
orderPrice integer not null,
primary key (ORDER_ITEM_ID)
)
Hibernate:
create table ORDERS (
ORDER_ID bigint not null,
MEMBER_ID bigint,
orderDate timestamp,
status varchar(255),
primary key (ORDER_ID)
)
각각의 테이블들이 만들어진다.
Member를 보면 MEMBER_ID가 PK로 잡혀있다.
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.CascadeType.*;
import static javax.persistence.FetchType.*;
@Entity
@Table(name = "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;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(LocalDateTime orderDate) {
this.orderDate = orderDate;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
}
Order Class에서 아래 부분이 조금 이상하다 느끼는데
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@Column(name = "MEMBER_ID")
private Long memberId;
private LocalDateTime orderDate;
JpaMain에서 주문을 em.find해서 찾았다고 하고
memberId를 찾은 다음에 또 em.find해서 memberId 찾은 걸 넣어서 넣어줘야 한다.
package jpabook.jpashop;
import jpabook.jpashop.domain.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Order order = em.find(Order.class, 1L);
Long memberId = order.getMemberId();
Member member = em.find(Member.class, memberId);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
이러한 데이터 중심 설계의 문제점은
- 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
- 테이블의 외래키를 객체에 그대로 가져옴
- 객체 그래프 탬색이 불가능
- 참조가 없으므로 UML도 잘못됨
id만 가지고 있어서 참조가 다 끊기게 된다.
<출처 김영한: 자바 ORM 표준 JPA 프로그래밍 - 기본편 >
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
참고: https://www.inflearn.com/questions/19302
'Spring > JPA' 카테고리의 다른 글
양방향 연관관계와 연관관계의 주인 (0) | 2022.05.08 |
---|---|
연관관계 매핑 기초 - 단방향 연관관계 (0) | 2022.04.16 |
기본 키 매핑 (0) | 2022.04.14 |
필드와 컬럼 매핑 (0) | 2022.04.14 |
데이터베이스 스키마 자동 생성 (0) | 2022.04.14 |