@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public void svae(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
JPA가 제공하는 @PersistenceContext
어노테이션을 통해 스프링이 생성한 엔티티 매니저에 JPA의 엔티티 매니저를 주입한다.
<aside> 💡 엔티티 매니저 팩토리 주입
@PersistenceUnit
private EntityManagerFactory emf;
</aside>
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
private final 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
모든 데이터 변경은 트랜잭션 안에서 수행되어야 하기 때문에 비즈니스 로직을 작성하는 서비스에 @Transactional
어노테이션을 달아준다.
트랜잭션에서 readOnly=true
옵션을 사용하면 영속성 컨텍스트를 플러시하지 않고 더티체킹을 하지 않는 등의 이점으로 약간의 성능 향상이 있다. 데이터 변경이 없는 읽기 전용 메서드에 사용한다.
MemberRepository 의존성 주입
필드 주입
@Autowired
MemberRepository memberRepository;
스프링 빈에 등록된 memberRepository를 주입해 준다. 단, 추천하지 않는 방식이다.
생성자 주입
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
변경 불가능한 안전한 객체 생성이 가능하므로 추천하는 방식이다.
생성자가 하나일 경우 @Autowired
어노테이션 생략이 가능하다.
final
키워드를 추가하면 컴파일 시점에 memberRepository를 설정하지 않는 오류를 체크할 수 있다.
lombok
@AllArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
}
모든 필드를 가지고 생성자를 생성한다.
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
}
final로 선언된 필드만 가지고 생성자를 생성한다.
<aside> 💡 실무에서는 검증 로직이 있어도 멀티 쓰레드 상황을 고려해서 회원 테이블의 회원명 컬럼에 유니크 제약 조건을 추가하는 것이 안전하다.
</aside>