[spring] 회원 도메인 개발

vector13·2022년 3월 28일
0

spring

목록 보기
13/25

회원 repository 개발 -class MemberRepository


@PersistenceUnit : 엔티티 메니터 팩토리( EntityManagerFactory ) 직접 주입 받을 수있음 .
그러나
@PersistenceContext : 엔티티 메니저( EntityManager ) 주입 하는 것이 있으므로 거의 쓸일 없을 것 .

em.persist(member); -> jpa가 저장하는 로직 된다.

++ 트랜젝션이 커밋되는 시점에 db에 반영됨 (insert쿼리 날라감)

단건조회

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

findAll의 createQuery은 Entity 객체를 대상으로 쿼리를 줘서, 테이블 단위로 쿼리 처리하는 sql과 조금 다름

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); //jpa가 저장하는 로직 된다.
    }

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

    //createQuery은 Entity 객체를 대상으로 쿼리를 줘서, 테이블 단위로 쿼리 처리하는 sql과 조금 다름
    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();
    }

}

회원 서비스 개발 -class MemberService


jpa 데이터 변경이나 로직들은 트랜젹션 안에서 해야한다!
( 그래야 Lazy도 가능.. ) @Transactional
이거는 javax랑 스프링거 중에서
import org.springframework.transaction.annotation.Transactional; 이거 쓰기

@Transactional(readOnly = true)옵션 주면
jpa 조회하는 곳에서는 성능 최적화 시켜준다.
읽기에는 이걸 넣어주면 좋다.

@Autowired : 스프링이 스프링빈에 들어있는 bean을 Injectiongownsek.

@Autowired
    private MemberRepository memberRepository;

로 쓰기도 하지만 테스트 등을 할 때 이름을 바꿀 수 없는 문제가 있으므로
settter Injection으로

private MemberRepository memberRepository;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

단점은 runtime에 누군가 바꿀 수 있음.
궁극적으로 생성자 injection이 좋다.

private MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

중간에 바꿀수있지도 않고
테스트 케이스 생성할 때 직접 주입시켜서 의존을 안놓치고 잘 챙길 수있음.

요새는 생성자 하나만 있는 경우에는 스프링이 자동으로 autowired 해준다.

필드 변경 할 이유 없어서 final 권장
private final MemberRepository memberRepository;

( final로 해두면 컴파일시점에 값 세팅 안한거 체크 가능 )

@AllArgsConstructor는 생성자 만들어주고,
@RequiredArgsConstructor은 final가진 거만 생성자 만들어준다.

package jpabook.jpashop.service;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
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 java.util.List;

@Service
@Transactional(readOnly = true)     //readOnly를 기본으로 두고
@RequiredArgsConstructor            //final 필드 생성자 만들어준다.
public class MemberService {

    private final MemberRepository memberRepository;

    /*@Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }*/

    /*회원가입 */
    @Transactional
    public Long join(Member member) {
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    //중복 회원 검증
    private void validateDuplicateMember(Member member) {
        //Exception
        List<Member> findMembers = memberRepository.findByName(member.getName());
        if (!findMembers.isEmpty()) {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        }
    }

    //회원 전체조회
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }


    public Member findOne(Long memberId) {
        return memberRepository.findOne(memberId);
    }
}

회원 기능 테스트


테스트 중 밑의 에러가 뜬다면
java.lang.IllegalStateException: Failed to load ApplicationContext

h2 데이터베이스 띄우는걸 까먹지 않았는지 확인 .

쿼리를 보면

insert가 없다.
이유는
persist할 때 Transactional의 기본이 Rollback이기 때문이다. (Commit이 아니라)
만일 @Rollback(flase)로 주면 insert 가 보인다.

Junit5에서는 밑의 코드 대신에

@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("예외가 발생해야 한다.");
    }

Junit5인 jupiter.api Package의 @Test 어노테이션의 경우 expected 속성이 제거되어 더 이상 @Test 어노테이션을 통한 Exception Test가 불가능

Junit5에서는 Assertions의 assertThrows를 이용하여 Junit4와 동일하게 Exception을 테스트

그러므로 아래의 코드로 진행

@Test
    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
        IllegalStateException thrown = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));
    }

최종 코드

package jpabook.jpashop.service;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.junit.jupiter.api.Test;
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.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
@WebAppConfiguration
class MemberServiceTest {
    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;
    @Autowired EntityManager em;

    @Test
    //@Rollback(value = false)
    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
    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
        IllegalStateException thrown = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));
    }

}
profile
HelloWorld! 같은 실수를 반복하지 말기위해 적어두자..

0개의 댓글

관련 채용 정보