MemberRepository, MemberService(+TDD, @Transactional)

KMS·2022년 4월 14일
0

SpringBoot + JPA

목록 보기
2/14

도메일(Domain), 서비스(Service), 리포지토리(Repository)?:

  • 도메인(Domain): 엔티티가 모여 있는 계층
  • 서비스(Service): 비즈니스 로직, 트랜잭션 처리가 모여 있는 계층
  • 리포지토(Repsoitory): JPA를 직접 사용하면서 DB와 값을 주고 받는 계층
  • 개발 순서: 도메인->서비스&리포지토리->테스트 케이스로 검증->배포

MemberRepository

@Repository
public class MemberRepository {

    private final EntityManager em;

    public MemberRepository(EntityManager em) {
        this.em = em;
    }

    public Long save(Member member) {
        em.persist(member);

        return member.getId();
    }

    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.username = :userName", Member.class)
                .setParameter("userName", name)
                .getResultList();
    }
}

1. @Repository 애노테이션: 스프링 빈 등록
2. EntityManager 생성자 관계 주입: @Autowired, @PersistenceContext 애노테이션 등을 통해 EntityManager 관계 주입이 가능하지만, 생성자를 이용한 관계 주입이 가장 선호되는 방식(참고: EntityManagerFactory는 생성자 외에 @PersistenceUnit 애노테이션으로 관계 주입 가능)
(관계 주입에 대한 내용은 이전에 Spring 학습할때 배웠으므로 해당 내용 참고)

MemberService

@Service
@Transactional(readOnly = true) //DB에서의 데이터의 변경이 없는 읽기 전용 메서드에 사용, 영속성 컨텍스트를 flush() 하지 않으므로 약간의 성능 향상
public class MemberService {

    private final MemberRepository memberRepository; //생성 이후에 변경할 일 X => final로 설정

    public MemberService(MemberRepository memberRepository) {
        //관계 주입
        this.memberRepository = memberRepository;
    }

    /**
     * 회원 가입
     */
    @Transactional //DB에서의 데이터를 변경 => 그러므로, readOnly = false (DEFAULT)
    public Long join(Member member) {
        validateDuplicateMember(member); //중복 회원 검증
        return memberRepository.save(member);
    }


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

    /**
     * 회원 하나만 존회
     */
    public Member findOne(Long id) {
        return memberRepository.findOne(id);
    }

    private void validateDuplicateMember(Member member) {
        //중복 회원이 존재하면 EXCEPTION
        if (!memberRepository.findByName(member.getUsername()).isEmpty()) {
            throw new IllegalStateException("이미 존재하는 회원입니다!");
        }
    }
}

1. @Service 애노테이션: 스프링 빈 등록
2-1. @Transactional: DB의 상태를 변경해주도록 하는 애노테이션. being(), commit()을 수행해줍니다.
2-2. @Transactional(readOnly=true): DB에서의 데이터의 변경이 없는 읽기 전용 메서드에 사용하며, 영속성 컨텍스트를 flush() 하지 않으므로 약간의 성능 향상이 있습니다
(참고: 클래스 전체에 @Transactional(readOnly=true)를 애노테이션을 추가 했으므로 해당 클래스의 모든 메서드에서 DB 접근은 읽기만 가능하지만, public Long join(Memeber member)에는 @Transactional(readOnly=false)가 있으며, readOnly=true가 오버라이딩 되기 때문에 DB의 데이터 값 변경이 가능합니다.)

Test

MemberRepositoryTest

@SpringBootTest
class MemberRepositoryTest {

    @Autowired
    MemberRepository memberRepository;


    @Test
    @Transactional //자동으로 DB를 rollback 시켜줌
    @Rollback(false) //애너테이션을 추가하면 rollback 하지 않고 DB에 값을 그대로 남겨둠
    void testMember() {
        //given
        Member member = new Member();
        member.setUsername("MemberA");

        //when
        Long savedId = memberRepository.save(member);
        Member findMember = memberRepository.findOne(savedId);

        //then
        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        /**
         * member랑 findMember는 영속성 컨텍스트에 존재
         * 둘다 식별자(id)가 같으므로, 영속성 컨텍스트에서는 같은 엔티티라고 처리
         * 그렇기 떄문에, Member에 equals()를 따로 설정해주지 않아도 Assertions.assertThat(findMember).isEqualTo(member)는 Pass
         * && findMember == member는 true입니다.
         */
        Assertions.assertThat(findMember).isEqualTo(member);
        Assertions.assertThat(findMember == member).isTrue();
    }
}

MemberServiceTest

@SpringBootTest
@Transactional
class MemberServiceTest {

    @Autowired
    MemberService memberService;

    @Autowired
    MemberRepository memberRepository;

    @Test
    void 회원가입() {
        //given
        Member member = new Member();
        member.setUsername("Kim");

        //when
        Long savedId = memberService.join(member);

        //then
        Assertions.assertEquals(member, memberService.findOne(savedId));
    }

    @Test
    void 중복회원() {
        //given
        Member memberA = new Member();
        memberA.setUsername("Kim");
        memberService.join(memberA);

        //when
        Member memberB = new Member();
        memberB.setUsername("Kim");

        //then
        Assertions.assertThrows(IllegalStateException.class, () -> {
            memberService.join(memberB);
        });
    }

}```
profile
Student at Sejong University Department of Software

0개의 댓글