[스프링부트 JPA 활용] 회원 도메인 개발

[스프링부트 JPA 활용] 회원 도메인 개발

애플리케이션 구현


목차#


  1. 회원 도메인 개발
  • 회원 리포지토리 개발
  • 회원 서비스 개발
  • 기능 테스트
  1. 상품 도메인 개발
  • 상품 엔티티개발(비즈니스 로직추가)
  • 상품 리포지토리 개발
  • 상품 서비스 개발
  1. 주문 도메인 개발
  • 주문, 주문상품 엔티티 개발
  • 주문 리포지토리 개발
  • 주문 서비스 개발
  1. 웹 계층 개발
  • 홈 화면과 레이아웃
  • 회원 등록
  • 회원 목록 조회
  • 상품 등록
  • 상품 목록
  • 상품 수정
  • 변경 감지와 병함(merge)
  • 상품 주문
  • 주문 목록 검색, 취소
  1. API 개발 기본
  • 회원 등록 API
  • 회원 수정 API
  • 회원 조회 API
  1. API 개발 고급
  • 조회용 샘플 데이터 입력
  • 지연 로딩과 조회 성능 최적화
  • 페이징과 한계 돌파
  • OSIV와 성능 최적화
  1. 다음으로
  • 스프링 데이터 JPA 소개
  • QueryDSL 소개
  • 마무리

회원 도메인 개발#


구현 기능#


  • 회원 등록
  • 회원 목록 조회

구현순서#


  1. 회원 엔티티 코드 다시 보기
  2. 회원 리포지토리 개발
  3. 회원 서비스 개발
  4. 회원 기능 테스트

회원 엔티티 코드 다시 보기#


java/jpabook/jpashop/domain/Member.java

	package jpabook.jpashop.domain;
	
	import lombok.Getter;
	import lombok.Setter;
	
	import javax.persistence.*;
	import java.util.ArrayList;
	import java.util.List;
	
	@Entity
	@Getter @Setter
	public class Member {
	
	    public Member() {
	    }
	
	    @Id @GeneratedValue
	    @Column(name = "member_id")
	    private Long id;
	
	    private String name;
	
	    @Embedded
	    private Address address;
	
	
	    @OneToMany(mappedBy = "member")
	    private List<Order> orders = new ArrayList<>();
	
	}

간단하게 id, name, address, 1:N 관계로 orders가 속성으로 있었고, 이를 사용해 리포지토리를 개발하도록 하겠습니다.

회원 리포지토리 개발#


리포지토리 디렉토리 생성#

java/jpabook/jpashop/repository 위치에 리포지토리 경로 생성

contact

회원 리포지토리 생성#

java/jpabook/jpashop/repository/MemberRepository.java

package jpabook.jpashop.repository;

import jpabook.jpashop.domain.Member;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;

@Repository
public class MemberRepository {

    @PersistenceContext
    private EntityManager em;

    public void save(Member member){
        em.persist(member);
    }

    public Member findOne(Long id){
        return em.find(Member.class, id);
    }

    public List<Member> findAll(){

        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }

    public List<Member> findByName(String name){
        return em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name",name).getResultList();
    }

}

@Repository#

스프링을 사용하기 때문에 @Repository 어노테이션을 리포지토리에 추가하게 되면, 컴포넌트 스캔에 의해서 자동으로 스프링 빈으로 관리가 되게 됩니다.

@PersistenceContext#

또한 JPA를 사용하므로 스프링에서 제공하는 @PersistenceContext로 private EntityManager em를 추가합니다. 그러면 스프링이 EntityManager 객체를 만들어서 주입(injection)해주게 됩니다.

만약 EntityManagerFactory를 직접 사용하고 싶다면 @PersistenceUnit을 사용하면 private EntityManagerFactory emf를 주입받을 수 있습니다.

이전 소스#


java/jpabook/jpashop/domain/Member.java

Member.java
package jpabook.jpashop.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter @Setter
public class Member {

    public Member() {
    }

    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String name;

    @Embedded
    private Address address;


    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();

}

java/jpabook/jpashop/domain/Address.java

Address.java
package jpabook.jpashop.domain;

import lombok.Getter;

import javax.persistence.Embeddable;

@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;
    }

}

java/jpabook/jpashop/domain/Order.java

Order.java
package jpabook.jpashop.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter @Setter
@Table(name = "orders")
public class Order {

    @Id @GeneratedValue
    @Column(name="order_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)  // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @JoinColumn(name = "member_id") // Order의 member가 수정되면 Order의 외래키 값이 변경됩니다.
    private Member member;


    // mappedBy 연관관계의 주인인 OrderItem의 order로 매핑 되어있다는 뜻
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();


    // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "delivery_id")
    private Delivery delivery;

    private LocalDateTime orderDate; //주문시간

    @Enumerated(EnumType.STRING) // EnumType.ORDINAL(숫자라 순서바뀌면 큰일)이 기본이지만 무조건 EnumType.STRING(문자 코드)
    private OrderStatus status; // 주문상태 [ORDER, CANCEL]


    //==연관관계 메서드 (양방향 연관관계시 추가)==//
    public void setMember(Member member){
        this.member = member;
        member.getOrders().add(this);
    }

    public void addOrderItem(OrderItem orderItem){
        this.orderItems.add(orderItem);
        orderItem.setOrder(this);
    }

    public void setDelivery(Delivery delivery){
        this.delivery = delivery;
        delivery.setOrder(this);
    }


}

java/jpabook/jpashop/domain/OrderItem.java

OrderItem.java
package jpabook.jpashop.domain;

import jpabook.jpashop.domain.item.Item;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter @Setter
public class OrderItem {

    @Id @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @JoinColumn(name = "item_id")
    private Item item;

    @ManyToOne(fetch = FetchType.LAZY) // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @JoinColumn(name = "order_id")
    private Order order;

    private int orderPrice; //주문 당시의 가격
    private int count; //주문 수량
}

java/jpabook/jpashop/domain/OrderStatus.java

OrderStatus.java
package jpabook.jpashop.domain;

import jpabook.jpashop.domain.item.Item;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter @Setter
public class OrderItem {

    @Id @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @JoinColumn(name = "item_id")
    private Item item;

    @ManyToOne(fetch = FetchType.LAZY) // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
    @JoinColumn(name = "order_id")
    private Order order;

    private int orderPrice; //주문 당시의 가격
    private int count; //주문 수량
}

java/jpabook/jpashop/domain/Delivery.java

Delivery.java
package jpabook.jpashop.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter @Setter
public class Delivery {

    public Delivery() {
    }

    @Id @GeneratedValue
    @Column(name = "delivery_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY  // ToOne은 fetch = FetchType.LAZY로 꼭 !!! 세팅
            , mappedBy = "delivery")
    private Order order;

    @Embedded
    private Address address;

    @Enumerated(EnumType.STRING)
    private DeliveryStatus status; //READY, COMP

}

java/jpabook/jpashop/domain/item/Item.java

Item.java
package jpabook.jpashop.domain.item;

import jpabook.jpashop.domain.Category;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
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<>();

}

java/jpabook/jpashop/domain/item/Album.java

Album.java
package jpabook.jpashop.domain.item;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("A") //구분값 A
@Getter @Setter
public class Album extends Item{
    private String artist;
    private String etc;
}

java/jpabook/jpashop/domain/item/Book.java

Book.java
package jpabook.jpashop.domain.item;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("B") //구분값 B
@Getter @Setter
public class Book extends Item{
    private String author;
    private String isbn;
}

java/jpabook/jpashop/domain/item/Movie.java

Movie.java
package jpabook.jpashop.domain.item;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("M") //구분값 M
@Getter @Setter
public class Movie extends Item{
    private String director;
    private String actor;
}

java/jpabook/jpashop/domain/Category.java

Category.java
package jpabook.jpashop.domain;

import jpabook.jpashop.domain.item.Item;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@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)  // ToOne은 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);
    }
}

참고#