[ JPA ] 회원 도메인 개발
도메인 분석, 설계를 다 했다면, 본격 개발을 시작해 보자
개발순서 : 레포지토리 계층 > 서비스 > 테스트 케이스 검증 > 계층에 적용
회원 레포지토리
repository 패키지를 먼저 만들고, MemberRepository java파일을 생성
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 // 스프링 빈으로 등록, JPA 예외를 스프링 기반 예외로 예외 변환
public class MemberRepository {
@PersistenceContext // 엔티티 매니저(EntityManager) injection
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();
// sql은 테이블을 대상으로 jpql은 엔티티를 대상으로
}
public List<Member> findByName(String name){
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name",name)
.getResultList();
}
}
회원 서비스
@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 IllegalSateException("이미 존재하는 회원입니다.");
}
}
/* 전체 회원 조회 */
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
@Transactional 의 default은 readonly = false
위의 서비스단은 읽는 메소드가 많기 때문에, 전체를 readonly = true로 묶고,
쓰기가 필요한 메소드만 따로 @Transactional으로 묶어 줬다.
위에 작성한 validation로직은 문제가 발생할 가능성이 높다.
검증 로직이 있다고 해도 멀티 쓰레드 상황을 고려해서 DB에 유니크제약조건을 걸어주는 등 한 번 더 걸러주는 것이 좋다.
reposirtory injection 할때 방법이 여러 가지가 있다.
위의 방법같이 필드주입방법
public class MemberService {
@Autowired
MemberRepository memberRepository;
...
}
setter injection 방법
@Autowired
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
생성자 주입 방법
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
가장 좋은 방법은 생성자 injection 이다!
요즘 스프링은 생성자가 하나일 경우 @Autowired 없어도 자동 injection으로 해준다.
여기에 롬복의 @AllArgsConstructor을 적용해 주면 필드의 모든 것을 가지고 생성자를 만들어주는 방법이다.
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
즉 이부분을 생략할 수 있다.
여기서 더 좋은 방법이 @RequiredArgsConstructor
final이 있는 필드만 가지고 생성자를 만들어주는 방법이다.
먼저 만들었던 repository에서도 생성자를 만들어 줄 수 있다.
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager em;
//스프링이 엔티티 매니저를 만들어서 여기에 주입해줌 > 이걸 생성자로 injection해줬다.
// 원래는 persistance를 써야하는데 spring data jpa가 @Autowired로 할수있게 해줬고
// RequiredArgsConstructor를 써서 @Autowired 안써도 사용할수있게 해줬다.
이렇게 일관성있게 사용하는 것이 좋다!