@Repository
@RequiredArgsConstructor
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
@PersistenceContext
: 엔티티 매니저(EntityManager
) 주입@PersistenceUnit
: 엔티티 매니저 팩토리(EntityManagerFactory
) 주입save()
: 저장findOne()
: 단건 조회findAll()
: 리스트 조회findByName()
: name
을 이용해서 조회@Service
@Transactional(readOnly = true)
public class MemberService {
@Autowired
private 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()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
/**
* 회원 전체 조회
*/
public List<Member> findMembers() {
return memberRepository.findAll();
}
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}
@Service
: 서비스임을 나타내는 어노테이션@Transactional
: 트랜잭션, 영속성 컨텍스트readOnly=true
: 데이터의 변경이 없는 읽기 전용 메서드에 사용한다. 영속성 컨텍스트를 플러시하지 않으므로 약간의 성능 향상! (읽기 전용에는 다 적용)@Autowired
Injection
를 많이 사용한다. join()
: 회원 가입findMembers()
: 회원 전체 조회findOne()
: id
로 회원 조회public class MemberService {
@Autowired
private MemberRepository memberRepository;
...
}
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
@Autowired
를 생략할 수 있다.final
키워드를 추가하면 컴파일 시점에 memberRepository
를 설정하지 않는 오류를 체크할 수 있다. (보통 기본 생성자를 추가할 때 발견한다.)@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
...
}
@RequiredArgsConstructor
: 초기화되지 않은 final
필드나, @NonNull
이 붙은 필드에 대해 생성자를 생성해준다.📌 테스트 요구사항
- 회원가입을 성공해야 한다.
- 회원가입할 때 같은 이름이 있으면 예외가 발생해야 한다. (중복회원 체크)
@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("bae");
//when
Long savedId = memberService.join(member);
//then
em.flush();
assertEquals(member, memberRepository.findOne(savedId));
}
@Test(expected = IllegalStateException.class)
public void 중복_회원_예외() throws Exception {
//given
Member member1 = new Member();
member1.setName("bae");
Member member2 = new Member();
member2.setName("bae");
//when
memberService.join(member1);
memberService.join(member2); //예외가 발생해야 한다!!!
//then
fail("예외가 발생해야 한다.");
}
}
@RunWith(SpringRunner.class)
: 스프링과 테스트 통합@SpringBootTest
: 스프링부트 띄우고 테스트 → 없으면 @Autowired
다 실패!!@Transactional
: 반복 가능한 테스트 지원테스트 성공!