연관된 엔티티를 함께 DB에서 조회해야 하면 , fetch join 또는 엔티티 그래프 기능을 사용한다.
@XToOne(OneToOne , ManyToOne) 관계는 기본이 즉시로딩 이므로 직접 지연로딩으로 설정해야 한다.
@XToMany 관계는 fetch = FetchType.LAZY가 기본이다.
컬렉션은 필드에서 초기화하자.
컬렉션은 필드에서 바로 초기화 하는 것이 안전하다.
null 문제에서 안전하다.
하이버네이트는 엔티티를 영속화 할 때 , 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경한다.
만약 getOrders()처럼 임의의 메서드에서 컬렉션을 잘 못 생성하면 하이버네이트 내부 메커니즘에 문제가 발생할 수 있다.
따라서 필드레벨에서 생성하는 것이 가장 안전하고 , 코드도 간결하다.
Member member = new Member();
System.out.println(member.getOrders().getClass());
em.persist(team);
System.out.println(member.getOrders().getClass());
//출력 결과classjava.util.ArrayListclassorg.hibernate.collection.internal.PersistentBag
도메인 분석 설계
연관관계 매핑 분석
엔티티 클래스 개발
Member
@Entity @Getter @Setter public class Member { @Id @GeneratedValue @Column(name = "member_id") private Long id; private String name; @Embedded private Address address; @OneToMany(mappedBy = "member") // Order 테이블(객체)의 member에 맵핑 되었다. private List<Order> orders = new ArrayList<>(); }
Address
@Embeddable @Getter public class Address { private String city; private String street; private String zipcode; protected Address() { } public Address(String city, String street, String zipcode) { this.city = city; this.street = street; this.zipcode = zipcode; } }
Order
@Entity @Table(name = "orders") @Getter @Setter public class Order { @Id @GeneratedValue @Column(name = "order_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; @OneToMany(mappedBy = "order" , cascade = CascadeType.ALL) private List<OrderItem> orderItems = new ArrayList<>(); @OneToOne(fetch = FetchType.LAZY , cascade = CascadeType.ALL) @JoinColumn(name = "delivery_id") private Delivery delivery; private LocalDateTime orderDate; private OrderStatus status; // 주문상태 [ORDER , CANCEL] //== 연관관계 메서드 == public void setMember(Member member){ this.member = member; member.getOrders().add(this); } public void addOrderItem(OrderItem orderItem){ orderItems.add(orderItem); orderItem.setOrder(this); } public void setDelivery(Delivery delivery){ this.delivery = delivery; delivery.setOrder(this); } }
OrderStatus
public enum OrderStatus { ORDER , CANCEL }
OrderItem
@Entity @Getter @Setter public class OrderItem { @Id @GeneratedValue @Column(name ="order_item_id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "item_id") private Item item; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; private int orderPrice; private int count; }
Delivery
@Entity @Getter @Setter public class Delivery { @Id @GeneratedValue @Column(name = "delivery_id") private Long id; @OneToOne(mappedBy = "delivery" , fetch = FetchType.LAZY) private Order order; @Embedded private Address address; @Enumerated(EnumType.STRING) // @Enumerated(EnumType.ORDINAL) ORDINAL은 숫자로 들어가니 꼭 STRING으로만 쓰자 // Enum이 추가되어 숫자가 밀릴 가능성이 있다. private DeliveryStatus status; // READY , COMP }
DeliveryStatus
public enum DeliveryStatus { READY , COMP }
Item
@Entity @Getter @Setter @Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 싱글 테이블 전략 @DiscriminatorColumn(name = "dtype") public abstract class Item { @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<>(); }
@Entity @Getter @Setter @DiscriminatorValue("A") public class Album extends Item { private String artist; private String etc; }
@Entity @Getter @Setter @DiscriminatorValue("B") public class Book extends Item { private String author; private String isbn; }
@Entity @Getter @Setter @DiscriminatorValue("M") public class Movie extends Item{ private String director; private String actor; }
Category , CategoryItem
@Entity @Getter @Setter public class Category { @Id @GeneratedValue @Column(name = "category_id") private Long id; private String name; @ManyToMany @JoinTable(name = "category_item" , joinColumns = @JoinColumn(name = "category_id"), inverseJoinColumns = @JoinColumn(name = "item_id")) private List<Item> items = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") private Category parent; @OneToMany(mappedBy = "parent") private List<Category> child = new ArrayList<>(); // == 연관관계 메서드 == public void addChildCategory(Category child){ this.child.add(child); child.setParent(this); } }
테이블 생성
2021-01-30 17:38:24.223 INFO 5828 --- [ restartedMain] p6spy : #1611995904223 | took 1ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table category ( category_id bigint not null, name varchar(255), parent_id bigint, primary key (category_id) ) 2021-01-30 17:38:24.225 INFO 5828 --- [ restartedMain] p6spy : #1611995904225 | took 1ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table category_item ( category_id bigint not null, item_id bigint not null ) 2021-01-30 17:38:24.229 INFO 5828 --- [ restartedMain] p6spy : #1611995904229 | took 3ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table delivery ( delivery_id bigint not null, city varchar(255), street varchar(255), zipcode varchar(255), status varchar(255), primary key (delivery_id) ); 2021-01-30 17:38:24.232 INFO 5828 --- [ restartedMain] p6spy : #1611995904232 | took 1ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table item ( dtype varchar(31) not null, item_id bigint not null, name varchar(255), price integer not null, stock_quantity integer not null, artist varchar(255), etc varchar(255), author varchar(255), isbn varchar(255), actor varchar(255), director varchar(255), primary key (item_id) ); 2021-01-30 17:38:24.235 INFO 5828 --- [ restartedMain] p6spy : #1611995904235 | took 1ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table member ( member_id bigint not null, city varchar(255), street varchar(255), zipcode varchar(255), name varchar(255), primary key (member_id) ); 2021-01-30 17:38:24.239 INFO 5828 --- [ restartedMain] p6spy : #1611995904239 | took 2ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table order_item ( order_item_id bigint not null, count integer not null, order_price integer not null, item_id bigint, order_id bigint, primary key (order_item_id) ); 2021-01-30 17:38:24.243 INFO 5828 --- [ restartedMain] p6spy : #1611995904243 | took 2ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop create table orders ( order_id bigint not null, order_date timestamp, status integer, delivery_id bigint, member_id bigint, primary key (order_id) ); 2021-01-30 17:38:24.253 INFO 5828 --- [ restartedMain] p6spy : #1611995904253 | took 8ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table category add constraint FK2y94svpmqttx80mshyny85wqr foreign key (parent_id) references category 2021-01-30 17:38:24.259 INFO 5828 --- [ restartedMain] p6spy : #1611995904259 | took 2ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table category_item add constraint FKcq2n0opf5shyh84ex1fhukcbh foreign key (category_id) references category 2021-01-30 17:38:24.263 INFO 5828 --- [ restartedMain] p6spy : #1611995904263 | took 3ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table order_item add constraint FKija6hjjiit8dprnmvtvgdp6ru foreign key (item_id) references item 2021-01-30 17:38:24.267 INFO 5828 --- [ restartedMain] p6spy : #1611995904267 | took 2ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table order_item add constraint FKt4dc2r9nbvbujrljv3e23iibt foreign key (order_id) references orders 2021-01-30 17:38:24.271 INFO 5828 --- [ restartedMain] p6spy : #1611995904271 | took 4ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table orders add constraint FKtkrur7wg4d8ax0pwgo0vmy20c foreign key (delivery_id) references delivery 2021-01-30 17:38:24.276 INFO 5828 --- [ restartedMain] p6spy : #1611995904276 | took 3ms | statement | connection 3| url jdbc:h2:tcp://localhost/~/jpashop alter table orders add constraint FKpktxwhj3x9m4gth5ff6bkqgeb foreign key (member_id) references member
엔티티 설계시 주의점
Member member = new Member(); System.out.println(member.getOrders().getClass()); em.persist(team); System.out.println(member.getOrders().getClass()); //출력 결과 class java.util.ArrayList class org.hibernate.collection.internal.PersistentBag
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런
실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다. 초급
www.inflearn.com
'기록 > 스프링 부트 와 JPA 활용' 카테고리의 다른 글