package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
/**
* 회원가입
* @param member
* @return Long
*/
@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("이미 존재하는 회원입니다.");
}
}
/**
* 회원 전체 조회
* @param
* @return List<Member>
*/
public List<Member> findMembers(){
return memberRepository.findAll();
}
/**
* 회원 단건 조회
* @param memberId
* @return Member
*/
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
}
@Service@Service
public class MemberService {
primary key - id@Transactional
public Long join(Member member){
validateDuplicateMember(member); //중복회원검증
memberRepository.save(member);
return member.getId();
}
public class MemberRepository {
...
public void save(Member member){
em.persist(member);
}
MemberRepository save 메서드의 em.persist(member)에서 영속성 컨텍스트에 멤버 객체를 올림
영속성 컨텍스트는 pk인 id 생성을 보장함
validateDuplicateMember()private void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if(!findMembers.isEmpty()){
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
name 컬럼에 unique 제약 조건을 추가하는 것이 안전함@Transactional@Transactional(readOnly = true)
public class MemberService {
...
@Transactional
public Long join(Member member){
...
jpa의 모든 데이터 변경 로직들은 transaction 안에서 이루어져야 함
findMembers와 findOne 메서드와 같이 조회만 수행하는 경우에는 readonly 옵션을 true로 설정
위 코드처럼 Service class에 @Transactional(readOnly = true)을 걸어놓고 join 메서드는 @Transactional로 선언을 하면,
join 메서드에 달린 어노테이션이 더 우선권을 가져서 join 시에는 readonly 옵션 없이 변경을 수행하게 됨만약 해당 서비스에서 쓰기 기능만 수행을 한다면 서비스에만 readonly 옵션이 없는 @Transactional을 붙이는 것이 좋음
@Transactional(readOnly = true)조회 최적화, 데어터의 변경이 없는 읽기 전용 메서드에 사용
영속성컨텍스트를 Dirty Checking(변경 감지) 하지 않음
Dirty Checkingflush하고 디비의 트랜잭션을 Commit해서 update와 같은 메서드를 사용하지 않고 Entity의 수정이 이루어지는 것commit했을 때 영속성 컨텍스트가 자동으로 flush되지 않기 때문에 조회용으로 가져온 entity의 예기치 못한 수정을 방지할 수 있음영속성 컨텍스트를 플러시 하지 않기 때문에 약간의 성능이 향상됨
Snapshot을 따로 보관하지 않기 때문에 메모리가 절약되는 성능상의 이점도 존재함데이터 변경이 필요한 것에 해당 옵션을 사용하면 변경이 안되기 때문에 주의해서 사용해야 함
@Autowired@Autowired
@Autowired
private MemberRepository memberRepository;
setter를 활용한 주입 방식private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
장점 : 테스트시 메서드를 통해 바로 주입이 가능함
단점 : 애플리케이션 로딩 시점에 조립이 모두 이루어지고 런타임에 변경이 이루어질 일은 없음
생성자 주입 방식
- 생성자 주입 방식을 권장
- 변경 불가능한 안전한 객체 생성이 가능해짐
- 생성자가 하나면
@Autowired를 생략할 수 있음final키워드를 추가하면 컴파일 시점에memberRepository를 설정하지 않는 오류를 체크할 수 있음(보통 기본 생성자를 추가할 때 발견)
private MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public static void main(String[] args) {
MemberService memberService = new MemberService(주입 필요);
}
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
생성자가 하나면 @Autowired를 생략할 수 있음
@Autowired 없이도 생성자가 하나만 있는 경우에는 자동으로 인젝션 해줌final 키워드를 추가하면 컴파일 시점에 memberRepository를 설정하지 않는 오류를 체크할 수 있음(보통 기본 생성자를 추가할 때 발견)
final로 선언해야 함또한 아래 세줄로 작성된 생성자 선언 코드는 @RequiredArgsConstructor로 대체 가능
final이 있는 필드만 가지고 생성자 선언이 가능해짐활용 전
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
활용 후
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager em;
EntityManager는 @PersistenceContext라는 표준 어노테이션이 있어야 인젝션이 가능함
@Autowired로도 인젝션이 되도록 지원을 해주고 있음따라서 서비스 코드와 같이 리포지토리의 엔티티 매니저를 생성자로 인젝션하도록 코드를 구현할 수 있음
일관성 있는 코드 작성이 가능해짐