inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

회원 서비스 개발

ValidateDuplicateMember 메소드와 트랜젝션 관련해서 질문입니다.

해결된 질문

684

준돌

작성한 질문수 3

2

@Transactional 애노테이션을 붙인 메서드는 오버라이딩 할 수 있어야 하기때문에 ValidateDuplicateMember 메서드를 private 접근지정자를 설정하면 명시적으로 @Transactional 애노테이션을 해당 메서드에 붙일 수가 없는데,

강사님께서 하신것처럼 클래스 레벨로 @Transactional(readOnly = true) 애노테이션을 붙이면 ValidateDuplicateMember 메서드는 트랜젝션이 적용이 안되는지 궁금합니다.

만약 적용이 안된다면 접근 지정자를 protected 로 변경해서 사용해도 되는지 궁금합니다.

----------------------- 전체 소스코드 입니다. -------------------------------

@Service
@Transactional
public class MemberService {

@Autowired
private MemberRepository memberRepository;

/**
* 회원가입
* @param member member entity
* @return memberId
*/
public Long join(Member member) {
ValidateDuplicateMember(member);
memberRepository.save(member);

/*
* member를 영속화 할때 key를 member의 id로 설정 (generateValue)
* member 객체의 id 필드에는 값이 채워져있는 것을 보장할 수 있음
*/
return member.getId();
}

@Transactional(readOnly = true)
protected void ValidateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}

/**
* 회원 전체 조회
* @return memberList
*/
@Transactional(readOnly = true) // 성능 최적화
public List<Member> findMembers() {
return memberRepository.findAll();
}

/**
* 회원 단건 조회
* @param memberId memberId
* @return findMember
*/
@Transactional(readOnly = true)
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}

java spring 웹앱 JPA spring-boot

답변 3

3

김영한

안녕하세요. 준석님

스프링의 트랜잭션은 AOP 방식으로 구성되는데요. 

AOP는 외부에서 호출할 때만 기능이 적용됩니다. 한마디로 외부에서 호출할 때만 트랜잭션이 적용됩니다.

예를 들어서 다음과 같을 때는 트랜잭션이 적용되지 않습니다.

class Service {

  public a() {

    b();

  }

  @Transactional

  public b(){}

}

호출 코드1: client.a(); //트랜잭션 적용X

- a()는 트랜잭션이 적용이 안된 상태로 호출이 되었는데, 그 안에서 b()를 호출해도 AOP는 외부에서 호출할 때만 영향을 주기 때문에 트랜잭션이 적용되지 않습니다. (AOP라는 것이 한번 감싸는 것인데, 그 안에서 자기들끼리 호출하면 전혀 효과가 없는 것이지요)

반면에 다음 코드는 트랜잭션이 적용됩니다.

class Service {

  @Transactional

  public a() {

    b();

  }

  public b(){}

}

호출 코드2: client.a(); //트랜잭션 적용O

트랜잭션의 범위는 a()가 시작할 때부터 a()가 끝날때 까지 모두 포함됩니다. 따라서 b()도 트랜잭션 범위안에 들어갑니다.

위에서 private 메서드에 @Transactional을 넣었을 때 아마 오류가 발생한 것은 이걸 넣어도 방금 설명드린 것 처럼 트랜잭션이 적용되지 않기 때문일거에요.

추가로 "스프링 트랜잭션 내부호출"로 검색해보시면 더 자세한 내용을 이해하실 수 있을거에요^^

도움이 되셨길 바래요^^

0

준돌

==============기존 소스코드=================
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
   ...

   private void ValidateDuplicateMember(Member member) {
    List<Member> findMembers = memberRepository.findByName(member.getName());
    if (!findMembers.isEmpty()) {
        throw new IllegalStateException("이미 존재하는 회원입니다.");
    }
   ...
}
==============================================

강사님께서는 해당 메서드의 접근지정자를 private으로 하시고 
클래스 범위에서 @Transactional(readOnly = true) 애노테이션을 붙이셨는데,




==============변경 소스코드1==================
@Service
@RequiredArgsConstructor public class MemberService { ... @Transactional(readOnly = true) private void ValidateDuplicateMember(Member member) { List<Member> findMembers = memberRepository.findByName(member.getName()); if (!findMembers.isEmpty()) { throw new IllegalStateException("이미 존재하는 회원입니다."); } } ... } ========================================== 보시는 것처럼, private 메서드에서 트랜젝션 애노테이션을 붙이면, Methods annotated with '@Transactional' must be overridable 라는 에러가 표시됩니다. ==============변경 소스코드2==================
@Service
@RequiredArgsConstructor public class MemberService { ... @Transactional(readOnly = true) protected void ValidateDuplicateMember(Member member) { List<Member> findMembers = memberRepository.findByName(member.getName()); if (!findMembers.isEmpty()) { throw new IllegalStateException("이미 존재하는 회원입니다."); } } ... } ========================================== 따라서 protected로 변경해주면 에러가 사라지는데, 질문1. 결론은 기존 소스코드에서 처럼 클래스안에 private 메서드가 존재하고 클래스레벨로 트랜잭션 어노테이션을 주었을때 private 메서드에 트랜잭션이 정말 적용되는지 궁금합니다. 음,, 변경 소스코드1 에서 처럼 private 메서드에 트랜잭션 애노테이션을 주면 에러가 나기때문에 이런 궁금증이 생겼습니다. 질문2. 추가적으로, 변경 소스코드2는 protected 로 변경해서 에러를 없앴는데 private 메서드에 트랜잭션 애노테이션을 적용할 때 접근지정자를 이렇게 바꿔서 적용을 해도되는지 궁금합니다. 질문을 너무 난해하게 해서 죄송합니다 ㅜㅜ..

0

김영한

안녕하세요. 준석님

준석님이 생각하는 방향으로 코드를 다시 적어주세요^^

그리고, 어디에서 어떤 호출을 하는 시점에 트랜잭션이 적용되는 것이 궁금한지 호출 단위로 궁금한 포인트를 적어주시겠어요?

sdk 설정 오류

0

49

2

오탈자 - @Transactional

0

55

1

src/test/resources 테스트 경로 문제

0

50

1

상품 등록후 H2 db 출력 순서 바꿀 수 있나요?

0

63

1

MemberRepositoryTest 실행오류

0

79

1

boot 4.x >>> trasasction rolled back log & p6spy(영한님, 수업 자료 업데이트 해주시면 감사하겠습니다!!)

1

183

2

강의 마지막 QueryDSL 사용 부분 질문있습니다

1

137

2

클라이언트에서 isbn과 author 수정 요청을 한 경우에 대해 질문드립니다.

0

51

1

도메인 모델 패턴 vs 트랜잭션 스크립트 패턴

0

71

1

기본 생성자

0

60

1

h2 DB 연결시 jdbc url 변경 이유가 궁금합니다.

0

100

1

멤버서비스테스트 부분에서 막힙니다.

0

164

4

실무에서도 EntityManager를 이용해서 많이 작업하는 편일까요?

0

116

1

초반에 h2 다운로드 과정 꼭 필요한가요?

0

118

2

자신 필드에도 get으로 접근하는 이유가 있을까요?

0

112

1

24분 27초 연관관계 편의 메서드 위치

0

113

1

단건 주문만 가능하게 한건 의도한 부분이신가요?

0

108

2

빌드 툴, Gradle

0

58

1

h2연결은 된 것 같은데 엔티티 테이블까지 작성 후 확인해보아도 테이블이 안보입니다

0

75

2

Repository에서 EntityManager 주입 방식 차이

0

88

1

롬복과 사용자 정의 setter 메서드

0

71

1

주문 목록 조회 fetch join 질문드립니다

0

81

1

dirty checking 질문드립니다.

0

81

1

동시성 관련 질문입니다

0

73

1