구현 기능
- 회원 등록
- 회원 목록 조회
순서
- 회원 엔티티 코드 다시 보기
- 회원 리포지토리 개발
- 회원 서비스 개발
- 회원 기능 테스트
회원 리포지토리 개발
MemberRepository 클래스
package repository;
import jpabook.jpashop.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Repository // componentscan에 의하여 스프링 빈으로 관리됨
public class MemberRepository {
// PersistenceContext는 스프링이 생성한 EntityManager를 자동으로 주입시켜줌
@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는 @Component 스캔의 대상이기 때문에 자동으로 스프링 빈에서 관리합니다.
- @PersistenceContext는 스프링이 생선한 EntityManager를 자동으로 주입시켜 줍니다.
참고로 EntityManagerFactory를 쓰고 싶다면 @PersistenceUnit을 사용하시면 됩니다.
회원 서비스 개발
MemberService
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import repository.MemberRepository;
import java.util.List;
@Service
@RequiredArgsConstructor // final로 이루어진 필드로만 생성자를 만들어 줌
public class MemberService {
private final MemberRepository memberRepository; // 한번 초기화하면 바꿀 일이 없으니 final 선언
/**
* 회원 가입
*/
@Transactional
public Long join(Member member){
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
/**
* 같은 이름이 있는 회원은 등록 안되도록
*/
private void validateDuplicateMember(Member member) {
//exception
// 이러한 경우 멀티스레드 환경에서 문제가 될 수 있다.
// 방지하기 위해서 member의 name을 unique 제약조건을 거는 것을 권장한다.
List<Member> findMembers = memberRepository.findByName(member.getName());
if(!findMembers.isEmpty()){
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
/**
* 회원 전체 조회
*/
@Transactional(readOnly = true) // 조회 성능 최적화
public List<Member> findMembers() {
return memberRepository.findAll();
}
/**
* 멤버 1명 조회
*/
@Transactional(readOnly = true) // 조회 성능 최적화
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
}
Spring Data JPA를 사용하지 않는 경우 @PersistenceContext를 사용해야 bean에서 injection시켜주지만, Spring Data JPA를 사용하는 경우 @PersistenceContext를 사용하지 않고 @Autowired만 사용해도 injection 시켜줍니다.
따라서, MemberRepository를 아래와 같이 변경할 수 있습니다.
회원 기능 테스트
MemberServiceTest
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
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.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import jpabook.jpashop.repository.MemberRepository;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class) // "junit을 실행할 때, 스프링과 엮어서 실행하겠다"를 의미합니다.
@SpringBootTest // SpringBoot를 띄운 상태에서 Test를 할 때 추가해야되는 어노테이션 (추가하지 않으면 @Autowired기능에서 에러가 발생합니다.)
@Transactional // 기본적으로 rollback
public class MemberServiceTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
// @Rollback(false) insert query 문을 보고싶다면 해보자
// 또는 EntityManager를 만들어서 flush() 해주면된다.
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//then
assertEquals(member, memberRepository.findOne(saveId));
}
@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("예외가 발생해야 한다.");
}
}
given, when, then 이란?
"given을 주고 when 상황이 있을 때, then 이러한 결과가 나올 것이다."의 flow로 이어지는 테스트 작성 방법입니다.
예외 테스트하는 방법
예외가 터져야하는 테스트라면 @Test에 옵션을 추가하여 코드를 간결화 할 수 있습니다.
아래와 같이 try, catch 문을 쓰지 않아도 됩니다.
In-Memory로 테스트 하는 방법
h2 Database 홈페이지에서 cheat sheet를 보시면,
In-Memory 설정하는 url이 있습니다. test를 할 땐 In-Memory로 하기위해
프로젝트 test 폴더에 resources 파일을 만들어 main에 있는 application.yml을 복사해서 저장해줍니다. 그리고 application.yml 코드 중 datasource url을 jdbc:h2:mem:test로 수정해주시면 In-Memory로 테스트를 할 수 있습니다.
참고로 Spring boot에서 database에 대한 별다른 설정이 없다면 memory DB로 돌리기 때문에 Database 설정에 대한 모든 코드를 지워도 memory 모드로 사용가능 합니다.
출처
'spring > 인프런 강의 정리' 카테고리의 다른 글
[JPA 활용 1] 6. 주문 도메인 개발 (0) | 2022.04.19 |
---|---|
[JPA 활용 1] 5. 상품 도메인 개발 (0) | 2022.04.18 |
[JPA 활용 1] 3. 애플리케이션 구현 준비 (0) | 2022.04.17 |
[JPA 활용 1] 2. 도메인 분석 설계 (0) | 2022.04.17 |
[JPA 활용 1] 1. 프로젝트 환경설정 (0) | 2022.04.17 |