회원 도메인 개발
Reference : Inflearn- 실전 스프링부트와 JPA 활용1 (김영한님 강의)
다음 기능들을 포함한 애플리케이션을 개발해보자.
회원 기능
회원 등록
회원 조회
상품 기능
상품 등록
상품 수정
상품 조회
주문 기능
상품 주문
주문 내역 조회
주문 취소
회원 리포지토리
MemberRepository.java
@Repository : 스프링빈으로 등록하고, JPA 예외를 스프링 기반 예외로 변환
@PersistenceContext : 엔티티 매니저 주입
@PersistenceUnit : 엔티티 매니저 팩토리 주입
package jpabook.jpashop.repository;
import jpabook.jpashop.domain.Member;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
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();
}
}
save() : persist로 멤버 엔티티 넣고, 트랜잭션이 커밋되는 시점에 db에 반영됨. (insert query가 날라감)
findOne() : 단건 조회. 멤버를 조회해서 반환
findAll() : JPQL을 사용해서 query 생성. (SQL과 비슷한데 FROM의 대상이 테이블이 아닌 엔티티가 된다.)
findByName() : Parameter binding해서 특정 이름의 회원만 찾는다.
회원 서비스
service package / MemberService.java
@Service : Component scan의 대상이 되어 자동으로 등록됨.
@Transactional : 트랜잭션, 영속성 컨텍스트.
readOnly=true : 데이터 변경이 없는 읽기 전용 메서드에 사용한다. 약간의 성능 향상 (읽기 모드만!!!)
데이터베이스 드라이버가 지원시 DB에서 성능 향상
기본적으로 두면 readOnly=false 이므로 필요한 상황에 따라 사용
@Autowired : 생성자 Injection 많이 사용
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
//회원 가입
@Transactional
public Long join(Member member){
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if(!findMembers.isEmpty()) {
//EXCEPTION
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
//회원 전체 조회
public List<Member> findMembers(){
return memberRepository.findAll();
}
//회원 단건 조회
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
}
중복 검증 시 실무에서는 멀티 쓰레드 상황을 고려하여 회원 테이블의 회원명 컬럼에 유니크 제약을 추가하는 것이 안전하다.
회원 기능 테스트
회원 가입에 성공해야함
회원 가입 시 같은 이름이 있으면 예외 발생
@RunWith(SpringRunner.class) : 스프링과 테스트 통합.
@SpringBootTest : 스프링 부트를 띄운 상태에서 테스트 실행. (있어야 @Autowired 실행)
@Transactional : 테스트 실행할때마다 트랜잭션 시작하고, 테스트 끝나면 트랜잭션을 강제로 롤백한다. 반복 가능한 테스트 지원.
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Autowired EntityManager em;
@Test
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long savedId = memberService.join(member);
//then
assertEquals(member, memberRepository.findOne(savedId));
}
@Test(expected = IllegalStateException.class)
public void 중복_회원_예외() throws Exception{
//given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
//when
memberService.join(member1);
memberService.join(member2); //예외가 발생해야 한다!!
//then
fail("예외가 발생해야 한다.");
}
}
★ 테스트 케이스 실행 시, Gvien, When, Then 사용해서 각각의 케이스 설정해서 작성하면 편하다!
test/resources/application.yml
spring:
logging:
level:
org.hibernate.SQL: debug
org.hibernate.type: trace
파일에 원하는 설정대로 하면 메모리 DB를 따로 사용하여 격리된 환경에서 테스트를 실행할 수 있다.
테스트 케이스를 위한 스프링 환겨과, 일반적으로 애플리케이션을 실행하는 환경이 다르므로 설정 파일을 다르게 사용하자.
위의 파일을 설정하면 테스트에서 스프링을 실행 시 이 위치에 있는 설정 파일을 읽는다.
(이 위치에 없으면 src/resources/application.yml 을 읽는다.)
Last updated