diff --git a/.DS_Store b/.DS_Store index 9b8abfe..2c03674 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/7.spring-DB DataAccess Application Skill/.DS_Store b/7.spring-DB DataAccess Application Skill/.DS_Store index c3f36d3..0139d06 100644 Binary files a/7.spring-DB DataAccess Application Skill/.DS_Store and b/7.spring-DB DataAccess Application Skill/.DS_Store differ diff --git a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/LogRepository.java b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/LogRepository.java index 908cf6d..a9760f7 100644 --- a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/LogRepository.java +++ b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/LogRepository.java @@ -3,7 +3,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import javax.persistence.EntityManager; import java.util.Optional; @@ -14,11 +16,11 @@ public class LogRepository { private final EntityManager em; - @Transactional +// @Transactional + @Transactional(propagation = Propagation.REQUIRES_NEW) public void save(Log logMessage){ log.info("log 저장"); em.persist(logMessage); - if(logMessage.getMessage().contains("로그예외")){ log.info("log 저장시 예외 발생"); throw new RuntimeException("예외 발생"); diff --git a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberRepository.java b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberRepository.java index c4da7e0..fa4b8ef 100644 --- a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberRepository.java +++ b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberRepository.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; diff --git a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberService.java b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberService.java index 08ed639..36165cb 100644 --- a/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberService.java +++ b/7.spring-DB DataAccess Application Skill/2.springtx/src/main/java/hello/springtx/propagation/MemberService.java @@ -3,14 +3,17 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @RequiredArgsConstructor +//@Transactional public class MemberService { private final MemberRepository memberRepository; private final LogRepository logRepository; + @Transactional public void joinV1(String username){ Member member = new Member(username); Log logMessage = new Log(username); @@ -24,6 +27,7 @@ public void joinV1(String username){ log.info("== logRepository 호출 종료 =="); } + @Transactional public void joinV2(String username){ Member member = new Member(username); Log logMessage = new Log(username); @@ -35,8 +39,7 @@ public void joinV2(String username){ try{ logRepository.save(logMessage); }catch (RuntimeException e){ - log.info("log 저장에 실패했습니다. logMessage={}", - logMessage.getMessage() ); + log.info("log 저장에 실패했습니다. logMessage={}", logMessage.getMessage() ); log.info("정상 흐름 반환"); } log.info("== logRepository 호출 종료 =="); diff --git a/7.spring-DB DataAccess Application Skill/2.springtx/src/test/java/hello/springtx/propagation/MemberServiceTest.java b/7.spring-DB DataAccess Application Skill/2.springtx/src/test/java/hello/springtx/propagation/MemberServiceTest.java index 2c1d3bc..097f23f 100644 --- a/7.spring-DB DataAccess Application Skill/2.springtx/src/test/java/hello/springtx/propagation/MemberServiceTest.java +++ b/7.spring-DB DataAccess Application Skill/2.springtx/src/test/java/hello/springtx/propagation/MemberServiceTest.java @@ -1,10 +1,13 @@ package hello.springtx.propagation; import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.UnexpectedRollbackException; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; @Slf4j @@ -14,6 +17,11 @@ class MemberServiceTest { @Autowired MemberRepository memberRepository; @Autowired LogRepository logRepository; + /** + * MemberService @Transactional: OFF + * MemberRepository @Transactional: ON + * LogRepository @Transcational: ON + */ @Test void outerTxOff_success(){ String username = "outerTxOff_success"; @@ -22,4 +30,103 @@ void outerTxOff_success(){ assertTrue(memberRepository.find(username).isPresent()); assertTrue(logRepository.find(username).isPresent()); } + + /** + * MemberService @Transactional: OFF + * MemberRepository @Transactional: ON + * LogRepository @Transactional: ON Exception + */ + @Test + public void outerTxOff_fail() throws Exception{ + //given + String username = "로그예외_outerTxOff_fail"; + //when + assertThatThrownBy(()->memberService.joinV1(username)) + .isInstanceOf(RuntimeException.class); + //then: 완전히 롤백되지 않고, member 데이터가 남아서 저장된다. + assertTrue(memberRepository.find(username).isPresent()); + assertTrue(logRepository.find(username).isEmpty()); + } + + /** + * MemberService @Transactional:ON + * MemberRepository @Transactional:OFF + * LogRepository @Transactional:OFF + */ + @Test + public void SingleTx() throws Exception{ + //given + String username = "singleTx"; + //when + memberService.joinV1(username); + //then + assertTrue(memberRepository.find(username).isPresent()); + assertTrue(logRepository.find(username).isPresent()); + } + + /** + * MemberService @Transactional:ON + * MemberRepository @Transactional:ON + * LogRepository @Transactional:ON + */ + @Test + public void outerTxOn_success() throws Exception{ + //given + String username = "outerTxOn_Success"; + //when + memberService.joinV1(username); + //then + assertTrue(memberRepository.find(username).isPresent()); + assertTrue(logRepository.find(username).isPresent()); + } + + /** + * MemberService @Transactional:ON + * MemberRepository @Transactional:ON + * LogRepository @Transactional:ON Exception + */ + @Test + public void outerTxOn_fail() throws Exception{ + //given + String username ="로그예외_outerTxOn_fail"; + //when + assertThatThrownBy(() -> memberService.joinV1(username)) + .isInstanceOf(RuntimeException.class); + //then + assertTrue(memberRepository.find(username).isEmpty()); + assertTrue(logRepository.find(username).isEmpty()); + } + + /** + * MemberService @Transactional:ON + * MemberRepository @Transactional:ON + * LogRepository @Transactional:ON Exception + */ + @Test + void recoverException_fail() { + //given + String username = "로그예외_recoverException_fail"; + //when + assertThatThrownBy(() -> memberService.joinV2(username)) + .isInstanceOf(UnexpectedRollbackException.class); + //then: 모든 데이터가 롤백된다. + assertTrue(memberRepository.find(username).isEmpty()); + assertTrue(logRepository.find(username).isEmpty()); + } + + /** + * MemberService @Transactional:ON + * MemberRepository @Transactional:ON + * LogRepository @Transactional(REQUIRES_NEW) Exception + */ + @Test + void recoverException_success() { + //given + String username = "로그예외_recoverException_success"; + //when + memberService.joinV2(username); + //then: member 저장, log 롤백 + assertTrue(memberRepository.find(username).isPresent()); + assertTrue(logRepository.find(username).isEmpty()); + } } \ No newline at end of file