스프링/6. 스프링 DB-2 59

60. 트랜잭션 전파 활용 7

저번에 만약 try catch로 복구를 시도하려 한다면 여러 고려를 했어야 했다. 근데 사실 간단한 것이 있다. REQUIRES_NEW 하면 그냥 저 트랜잭션만 분리시킬 수 있기 때문에, 다른 트랜잭션에 영향이 안간다. @Slf4j @Repository @RequiredArgsConstructor public class MemberRepository { private final EntityManager em; @Transactional public void save(Member member){ log.info("member 저장"); em.persist(member); } public Optional findByUsername(String username){ return em.createQuery("se..

59. 트랜잭션 전파 활용 6

그럼 만약, 위의 상황에서 MemberService에서 try catch로 에러를 잡아서 복구시키려 한다면? 우선 트랜잭션에 대한 rollbackOnly를 풀어야 할 듯? 뭐 쓰레드로컬로 접근해서 가져오는 거니, 따로 생성하는게 아니라 static? 그런 것에 접근할 듯? 일단 비즈니스에 예를 들면, 회원가입을 하는 데 만약 로그남기는 것에서 오류가 발생해서 회원가입을 못하게 하면.. 정말 비즈니스 적으로 비효율적이다. 이럴경우, LogRepository에서 예외를 일으켜 rollback을 해도, 저녀석만 rollback하고 나머지는 제대로 Member 등록이 되도록 하는 게 좋다. 근데 일단, 그냥 LogRepository의 save의 트랜잭션 옵션을 REQUIRES_NEW로 하면 된다. 그럼 트랜잭션..

58. 트랜잭션 전파 활용 5

전은 commit의 경우만 알아봤는데, 이제 rollback의 경우를 직접 실습해 보자. REQUIRED이므로 묶여져 있다고 생각하고, 하나만 rollback이 되도 모두 rollback이다. 롤백 시 내부 트랜잭션들은 rollback해도 물리 트랜잭션을 rollback하는 것이 아니라 rollbackOnly로 마크를 새겨둠. 저 커넥션? 트랜잭션 동기화 매니저? 에다가. 외부 트랜잭션이 commit이나 rollback 해야 진짜 commit이나 rollback임.

57. 트랜잭션 전파 활용 4

이제야 본격적으로 트랜잭션 전파를 활용하기 시작했다. 리마인드 시켜보자면, 트랜잭션을 논리 트랜잭션과 물리 트랜잭션이라는 관점으로 나누면 상당히 관리하기가 편하다. 논리 트랜잭션은 말 그대로 어떤 논리적인 트랜잭션, 어.. 논리적이지, 실제로는 적용을 아직 안 했을 수도 있다는 그런 뜻이다. 논리적으로는 적용을 한 것이다. 물리 트랜잭션은 실제 물리적으로 트랜잭션을 적용 하는 것 이다. 그리고, 이 물리 트랜잭션은 가장 외부의 트랜잭션이나, REQUIRE_NEW 해서 새롭게 트랜잭션을 생성한 트랜잭션이나, 여튼 첫번째 트랜잭션만이 이 물리 트랜잭션을 다룰 수 있다. 이 물리 트랜잭션이 commit 혹은 rollback을 해야 비로소 진정 트랜잭션이 끝나는 것이다. 다른 것은 그냥 논리적으로 commit을..

56. 트랜잭션 전파 활용 3

MemberRepository와 LogRepository를 묶는 가장 간단한 방법은 Service에서 트랜잭션 하는 것이다. 몇가지 경우가 있는데, 먼저 아예 MemberRepository와 LogRepository에 있던 @Transactional을 주석처리 하고, Service에만 @Transactional을 추가해 @Transactional public void joinV1(String username){ Member member = new Member(username); Log logMessage = new Log(username); log.info("== memberRepository 호출 시작 =="); memberRepository.save(member); log.info("== member..

55. 트랜잭션 전파 활용 2

트랜잭션 동작에 대해 볼거임. 현재는 이런 상황. 그냥 트랜잭션 한 구역 다 끝내고, 다음 트랜잭션 시작해서 다 끝내는 거라 완전히 남인 트랜잭션임. 주황색이 트랜잭션 범위. 그래서 @Test void outerTxOff_fail(){ String username = "로그예외_outerTxOff_fail"; memberService.joinV1(username); assertTrue(memberRepository.findByUsername(username).isPresent()); assertTrue(logRepository.findByMessage(username).isPresent()); } 우리가 message에 "로그예외"라는 글자가 있으면 runtimeException을 날리도록 해 놨는데, ..

54. 트랜잭션 전파 활용 1

비즈니스 요구사항 회원 등록, 조회 회원 데이터가 변경될 때 변경 이력을 DB LOG로 남겨야 함. 일단 등록 시에만 LOG 남기도록 함. 도메인부터 새로 만듦. @Entity @Getter @Setter public class Member { @Id @GeneratedValue private Long id; private String username; public Member() { } public Member(String username) { this.username = username; } } Long id 가 @Id이고, @GeneratedValue 줘서 이거는 DB나 어디선가 할당해 주는 값을 받는 거임. 이거 안 넣어주면 우리가 Id 직접 넣어줘야 함. JPA에서 기본 생성자(비어 있는 거) ..

53. 트랜잭션 전파 6

다양한 전파 옵션에 대해 알아보겠다. 보통 REQUIRED를 기본으로 쓰고, 아주 가끔 REQUIRES_NEW를 쓴다. REQUIRED 기존 트랜잭션에 참여 없으면 새로 생성 REQUIRES_NEW 항상 새 트랜잭션을 생성. SUPPORT 기존 트랜잭션 있으면 참여 없으면 아예 트랜잭션 없이 진행 말 그대로 지원 정도 NOT_SUPPORT 기존 트랜잭션 있을 시에도 트랜잭션 없이 진행하되, 기존 트랜잭션은 보류시킴. suspend 그거 인 듯 없을 시 그냥 트랜잭션 없이 진행 MANDATORY 기존 트랜잭션에 참여한다. 없으면 예외 발생. IllegalTransactionStateException 의무적인, 이란 뜻으로 반드시 트랜잭션이 있어야 한다. NEVER 기존 트랜잭션 있으면 예외 발생 없을 시 ..

52. 트랜잭션 전파 5

지금까지는 트랜잭션이 한몸이었는데 이번에는, 예를 들자면 외부 트랜잭션은 rollback이지만 내부 트랜잭은 commit인? 그 반대의 상황도. 알아보겠다. 그러니까, 코드상 동일한 시간선상에서 돌아간다고 하더라도, 각자 다른 트랜잭션 이다. 서로 각각의 물리 트랜잭션을 사용하도록. 이럴 땐 그냥 REQUIRES말고(이게 기본값임) REQUIRES_NEW 옵션을 사용하면 된다. @Test void inner_rollback_requires_new(){ log.info("외부 트랜잭션 시작"); TransactionStatus status1 = txManager.getTransaction(new DefaultTransactionAttribute()); log.info("외부 트랜잭션 .isNewTransa..

51. 트랜잭션 전파 4

이번엔 외부 트랜잭션이 롤백되는 경우를 보자. @Test void outerRollback(){ log.info("외부 트랜잭션 시작"); TransactionStatus status1 = txManager.getTransaction(new DefaultTransactionAttribute()); log.info("외부 트랜잭션 .isNewTransaction()={}", status1.isNewTransaction()); log.info("내부 트랜잭션 시작"); TransactionStatus status2 = txManager.getTransaction(new DefaultTransactionAttribute()); log.info("내부 트랜잭션 .isNewTransaction()={}", sta..