현재 SpringJpa 프로젝트를 하며 겪고있는 문제점들을 줄여나가 보고자 개인 JPA 컨벤션을 만들어보고자 한다.
REQUIRES_NEW 를 사용할 경우 생길 수 있는 이슈 1번
아래 코드를 살펴보자
@Transactional
public String test(){
Member testMember1 = repository.findByMemberByMemberId(2L);
handler.innerTest();
testMember1.changeAge(35);
return null;
}
test() 메서드는 컨트롤러에서 호출 된후 innerTest 라는 메소드를 호출한다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerTest(){
Member testMember2 = memberRepository.findByMemberByMemberId(2L);
testMember2.changeName("Patrick");
}
현재 코드 실행전 데이터베이스에 들어가있는 데이터는 아래와 같다
2번 멤버의 이름은 테스트이며 나이는 33이다
데이터베이스는 마리아디비를 사용했으며 격리수준 2단계 REPEATABLE_READ 가 적용돼있다.
결과는 3번
사실 메소드 2개만 꼴랑 있을경우 위같은 코드에서 생기는 문제는 금방 찾아낼 수 있다 하지만 원리를 알아도
위와같이 코드가 작성되어있고 트랜젝션이 4중 5중으로 들어가면 어디서 바뀌고 안바뀌는지 일일이 찾아 헤매야할것이다.
위코드 실행결과는 REQUIRES_NEW에서 적용한 코드는 단 한~~개도 반영되지 않는다이다.
이유를 살펴보자
쉽게 설명하기 위하여 test() 에서 오픈한 트랜젝션을 T1 innerTest() 에서 오픈한 트랜젝션을 T2라 하자
T1 에서 열어놓은 트랜젝션을 살펴보면
사실 이와같은 케이스가 메서드가 2개일경우 참 바보같은 실수를 하였구나 이런걸 내가 왜하냐 라고 생각했을때 코드가 고도화 되고 트랜젝션이 엉키고 엉키다 보면 이런일은 언제든지 발생할 수 있다.
REQUIRES_NEW 를 사용할 경우 생길 수 있는 이슈 2번
1번 이슈에서 볼수 있듯 아니 그러면 REQUIRES_NEW 에서 사용한 맴버 객체를 리턴하면 되지 않습니까!
결론 -> 이방법도 좋지는 않다 쓰지 말자
현재 디비 상태는 이렇다.
@Transactional
public String test(){
Member testMember1 = handler.innerTest();
testMember1.changeAge(35);
return null;
}
위와 같이 test() 메소드는 innerTest() 메소드를 호출한다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Member innerTest(){
Member testMember2 = memberRepository.findByMemberByMemberId(2L);
testMember2.changeName("Patrick");
return testMember2;
}
위 코드가 종료될시 결과값을 예측해보자
1. REQUITES_NEW 가 실행되고 test에서 change를 했으니 이름은 Patrick 이고 나이는 35로 바뀔것이다.
2. 뭔지 모르겠지만 에러가 난다.
3. 나이만 35로 바뀔것이다.
4. 이름만 바뀐다.
정답은 4번 이다.
그 이유는 test() T1 트랜젝션이 시작할때 T1 컨테이너에는 2번 맴버를 영속성컨텍스트에 저장한적이 없다.
특 T1 컨테이너는 2번 맴버가 영속성 컨텍스트에 없다. T2 에서 2번 맴버를 저장하고 디비에 플러시 하고 T1 트랜젝션 컨테이너에 맴버를 반환한다고 해서 T1 영속성컨텍스트에 올라가는것이 절!대 아니다.
하이버네이트 사용시 같은 아이디가 다른 영속성컨테이너에서 다른주소값으로 수정되는 상황은 무조건 피하도록하자.