[자바 ORM 표준 JPA] JPA 실전 예제 5 - 연관관계 관리
[자바 ORM 표준 JPA] JPA 실전 예제 5 - 연관관계 관리
실전 예제 5 - 연관관계 관리
글로벌 페치 전략 설정#
- 모든 연관관계를 지연로딩으로
- @ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 지연로딩으로 변경하길 권장
글로벌 페치 전략 예제소스에 적용#
기존 작성했던 엔티티중 @ManyToOne, @OneToOne에는 (fetch = FetchType.LAZY)를 추가한다.
Album.java
package jpabasic.jpashop.domain;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue("A")
public class Album extends Item{
private String artist;
private String etc;
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getEtc() {
return etc;
}
public void setEtc(String etc) {
this.etc = etc;
}
}
BaseEntity.java
package jpabasic.jpashop.domain;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@MappedSuperclass
public abstract class BaseEntity {
@Column(name = "REG_ID")
private String createBy;
@Column(name = "REG_DT")
private LocalDateTime createDate;
@Column(name = "MOD_ID")
private String LastModifiedBy;
@Column(name = "MOD_DT")
private LocalDateTime LastModifiedDate;
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public LocalDateTime getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDateTime createDate) {
this.createDate = createDate;
}
public String getLastModifiedBy() {
return LastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
LastModifiedBy = lastModifiedBy;
}
public LocalDateTime getLastModifiedDate() {
return LastModifiedDate;
}
public void setLastModifiedDate(LocalDateTime lastModifiedDate) {
LastModifiedDate = lastModifiedDate;
}
}
Book.java
package jpabasic.jpashop.domain;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue("B")
public class Book extends Item{
private String author;
private String isbn;
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
}
Category.java - fetch = FetchType.LAZY 적용
package jpabasic.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Category extends BaseEntity{
@Id @GeneratedValue
@Column(name = "CATEGORY_ID")
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY) // **
@JoinColumn(name = "parent_id")
private Category parent; // 상위 카테고리
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>(); // 자식 카테고리
@ManyToMany
@JoinTable(name = "CATEGORY_ITEM",
joinColumns = @JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name ="ITEM_ID")
)
private List<Item> items = new ArrayList<>();
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
}
Delivery.java - fetch = FetchType.LAZY 적용
package jpabasic.jpashop.domain;
import javax.persistence.*;
@Entity
public class Delivery extends BaseEntity{
@Id @GeneratedValue
@Column(name = "DELIVERY_ID")
private Long id;
private String city;
private String street;
private String zipcode;
@Enumerated(EnumType.STRING)
private DeliveryStatus status;
@OneToOne(mappedBy = "delivery" , fetch = FetchType.LAZY) // **
private Order order;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
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;
}
public DeliveryStatus getStatus() {
return status;
}
public void setStatus(DeliveryStatus status) {
this.status = status;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
DeliveryStatus.java
package jpabasic.jpashop.domain;
public enum DeliveryStatus {
ORDER, CANCEL
}
Item.java
package jpabasic.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item extends BaseEntity{
public Item(){
}
public Item(String name, int price, int stockQuantity) {
this.name = name;
this.price = price;
this.stockQuantity = stockQuantity;
}
@Id @GeneratedValue
@Column(name="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
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;
}
}
Member.java
package jpabasic.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member extends BaseEntity{
public Member(){}
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
private String name;
private String cicy;
private String street;
private String zipcode;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>(); //관례상 초기 값을 두어 NullPointer Exception을 방지
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 getCicy() {
return cicy;
}
public void setCicy(String cicy) {
this.cicy = cicy;
}
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;
}
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
}
Movie.java
package jpabasic.jpashop.domain;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue("M")
public class Movie extends Item{
private String director;
private String actor;
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
}
Order.java - fetch = FetchType.LAZY 적용
package jpabasic.jpashop.domain;
import javax.persistence.*;
import jpabasic.jpashop.domain.Member;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "ORDERS") // DB에 따라 ORDER가 예약어일 경우가 있어 ORDERS
public class Order extends BaseEntity{
public Order(){}
@Id
@GeneratedValue
@Column(name="ORDER_ID")
private Long id;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@ManyToOne(fetch = FetchType.LAZY) // **
private Member member;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>(); //관례상 초기 값을 두어 NullPointer Exception을 방지
@OneToOne(fetch = FetchType.LAZY) // **
@JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
public void addOrderItem(OrderItem orderItem) {
this.orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void addMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
}
OrderItem.java - fetch = FetchType.LAZY 적용
package jpabasic.jpashop.domain;
import javax.persistence.*;
import java.util.List;
@Entity
public class OrderItem extends BaseEntity{
public OrderItem(){
}
@Id @GeneratedValue
@Column(name = "OFDER_ITEM_ID")
private Long id;
private int orderPrice;
private int count;
@ManyToOne(fetch = FetchType.LAZY) // **
private Order order;
@ManyToOne(fetch = FetchType.LAZY) // **
private Item item;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
}
OrderStatus.java
package jpabasic.jpashop.domain;
public enum OrderStatus {
ORDER, CANCEL
}
JpaMain.java - 애플리케이션 재시작
package jpabasic.jpashop;
import jpabasic.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("jpashop");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Book book = new Book();
book.setName("JPA");
book.setAuthor("김영한");
em.persist(book);
tx.commit();
}catch (Exception e){
e.printStackTrace();
tx.rollback();
}finally {
em.close();
}
emf.close();
}
}
영속성 전이 설정#
주문(Order)을 생성할 당시 배송정보(Delivery), 그리고 주문상품(OrderItem)을 같이 생성한다는 뜻
- Order -> Delivery를 영속성 전이 ALL 설정
- Order -> OrderItem을 영속성 전이 ALL 설정
package jpabasic.jpashop.domain;
import javax.persistence.*;
import jpabasic.jpashop.domain.Member;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "ORDERS") // DB에 따라 ORDER가 예약어일 경우가 있어 ORDERS
public class Order extends BaseEntity{
public Order(){}
@Id
@GeneratedValue
@Column(name="ORDER_ID")
private Long id;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL) // **
private List<OrderItem> orderItems = new ArrayList<>(); //관례상 초기 값을 두어 NullPointer Exception을 방지
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) // **
@JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
public void addOrderItem(OrderItem orderItem) {
this.orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void addMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
}
설계시 Delivery의 라이프 사이클을 따로 관리 해야하는 것도 고민을 해볼 필요가 있습니다. 복잡성이나, 비즈니스 상황에 따라 Cascade를 적용할지 뺄지 정해야 합니다.