스프링 DB 2편(데이터 접근 활용 기술)

Ch11. 스프링 트랜잭션 전파(활용) - 예제 프로젝트 시작

webmaster 2022. 7. 8. 16:59
728x90

Member

@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String username;

    public Member(){
    }

    public Member(String username) {
        this.username = username;
    }
}

MemberRepository

@Slf4j
@Repository
@RequiredArgsConstructor
public class MemberRepository {

    private final EntityManager em;

    @Transactional
    public void save(Member member){
        log.info("member 저장");
        em.persist(member);
    }

    public Optional<Member> find(String username){
        return em.createQuery("select m from Member m where m.username = :username", Member.class)
            .setParameter("username", username)
            .getResultList().stream().findAny();
    }
}

Log

@Entity
@Getter
@Setter
public class Log {

    @Id @GeneratedValue
    private Long id;

    private String message;

    public Log(){

    }

    public Log(String message) {
        this.message = message;
    }
}

LogRepository

@Slf4j
@Repository
@RequiredArgsConstructor
public class LogRepository {

    private final EntityManager em;

    @Transactional
    public void save(Log logMessage){
        log.info("log 저장");
        em.persist(logMessage);

        if(logMessage.getMessage().contains("로그예외")){
            log.info("log 저장시 예외 발생");
            throw new RuntimeException("예외 발생");
        }
    }

    public Optional<Log> find(String message){
        return em.createQuery("select l from Log l where l.message = :message", Log.class)
            .setParameter("message", message)
            .getResultList().stream().findAny();
    }
}
  • 중간에 예외 상황을 재현하기 위해 로그 예외라고 입력하는 경우 예외를 발생시킨다

MemberService

@Slf4j
@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;
    private final LogRepository logRepository;

    public void joinV1(String username){
        Member member = new Member(username);
        Log logMessage = new Log(username);

        log.info("== memberRepository 호출 시작 ==");
        memberRepository.save(member);
        log.info("== memberRepository 호출 종료 ==");

        log.info("== logRepository 호출 시작 ==");
        logRepository.save(logMessage);
        log.info("== logRepository 호출 종료 ==");
    }

    public void joinV2(String username){
        Member member = new Member(username);
        Log logMessage = new Log(username);

        log.info("== memberRepository 호출 시작 ==");
        memberRepository.save(member);
        log.info("== memberRepository 호출 종료 ==");

        log.info("== logRepository 호출 시작 ==");
        try {
            logRepository.save(logMessage);    
        }catch (RuntimeException e){
            //로그 저장 실패시, 예외처리를 해준다.
            log.info("log 저장에 실패했습니다. logMessage={}", logMessage.getMessage());
            log.info("정상 흐름 반환");
        }
        log.info("== logRepository 호출 종료 ==");
    }
}
  • 회원을 등록하면서 동시에 회원 등록에 대한 DB 로그도 함께 남긴다.
  • joinV1()
    • 회원과 DB로그를 함께 남기는 비즈니스 로직이다.
    • 현재 별도의 트랜잭션은 설정하지 않는다.
  • joinV2()
    • joinV1()과 같은 기능을 수행한다.
    • DB 로그 저장 시 예외가 발생하면 예외를 복구한다.
    • 현재 별도의 트랜잭션은 설정하지 않는다

MemberServiceTest

@Slf4j
@SpringBootTest
class MemberServiceTest {


    @Autowired
    MemberService memberService;
    @Autowired
    MemberRepository memberRepository;
    @Autowired
    LogRepository logRepository;

    /**
     * memberService    @Transactional:OFF
     * memberRepository @Transactional:ON
     * logRepository    @Transactional:ON
     */
    @Test
    public void outerTxOff_success() {
        //given
        String username = "outerTxOff_success";

        //when
        memberService.joinV1(username);

        //then : 모든 데이터가 정상 저장된다.
        assertTrue(memberRepository.find(username).isPresent());
        assertTrue(logRepository.find(username).isPresent());
    }
}
728x90