트랜잭션 수동제어
스프링 및 스프링부트는 Mybatis의 트랜잭션을 자동으로 처리해준다.
하지만 로직에 따라 직접 제어해야할 때가 있을 것이다.
스프링에서의 트랜잭션
- SqlSession
- Mybatis의 클래스
- DB와의 연결 및 SQL문 처리를 수행한다. - SqlSessionTemplate
- Mybatis-Spring 연동 모듈 중 하나
- SqlSession을 스프링의 IoC컨테이너에서 관리하도록 함으로써 객체 생성과 관리, 트랜잭션등을 처리한다.
>> 장점 : 코드가 간결해지고 자원누수를 막는다.
>> 단점 : .commit(), .rollback()을 쓸수 없다! (직접 트랜잭션 관리 불가)
@Transactional
- 트랜잭션을 수동으로 제어하게끔 스프링에서 제공하는 어노테이션
- 일반적으로 비즈니스 로직을 수행하는 서비스 계층에서 작성한다.
- 어노테이션을 쓰지 않으면?
하나의 SQL문을 실행할때 성공하면 commit되고, 오류발생하면 rollback된다. - 어노테이션을 쓰면?
메소드 내에 존재하는 모든 SQL문은 하나의 트랜잭션에서 관리된다.
@Transactional 속성
- propagation
: 트랜잭션 전파 옵션을 정의
** 트랜잭션 전파 옵션이란? 트랜잭션에서 다른 트랜잭션을 호출할 때 어떠한 방식으로 처리할지 설정하는 것 - timeout / timeoutString
: 트랜잭션 제한 시간을 정의. 제한 시간을 초과하면 예외를 발생시킨다. - readOnly
: 현재 트랜잭션이 읽기 전용인지 읽기-쓰기 전용인지 정의 - rollbackFor / rollbackForClassName
: 지정한 예외가 발생하면 롤백되도록 Throwable 클래스를 정의 - noRollbackFor / noRollbackForClassName
: Transactional 어노테이션을 적용한 메소드에서 또다른 Transactional 가 적용된 메소드를 호출하면, 호출된(callee) 트랜잭션은 호출한(callser) 트랜젝션에 병합된다. callee 트랜잭션을 수행하다가 에러가 발생했을 경우 모든 트랜잭션은 rollback처리된다.
이때, 지정한 예외가 발생하더라도 롤백되지 않도록 상기 속성으로 Throwable 클래스를 정의
@Transactional 기본 롤백 조건
- Runtime Exception
- 개발자가 처리하기 어려운 예외. 프로그램 실행 중에 발생
- 자동으로 롤백된다.
- 롤백을 원하지 않으면 try/catch 등으로 예외처리를 해준다. - Error
- Exception이 아닌 경우로 시스템 메모리 부족처럼 예측 및 처리가 어렵다.
- 자동으로 롤백된다. - Checked Exception
- 프로그램이 제어할 수 없지만 개발자가 충분히 처리 가능한 예외
ex) SQLException, ...
- 개발자가 직접 처리할 수 있기 때문에 자동 롤백 대상이 아님
- 롤백 필요시 rollbackFor 속성을 기재해야한다.
**모든 예외상황에서 롤백하기 : @Transactional(rollbackFor = {Exception.class})
활용 예제 : 두 테이블에 동시에 insert하기
- Service단 메소드에 @Transactional을 붙이고 rollbackFor 조건으로 Exception.class를 붙인다.
- 롤백이 되길 원하는 조건에서 throw new Exception("전달메세지"); 를 실행시킨다.
- 발생한 예외에 대해서 Service단은 thorow 시키고 Controller단에서 try/catch한다.
- Contoller에서는 Service측 메소드에서 예외가 발생했다면 실패, 발생하지 않았다면 성공임을 기반으로 코드를 작성한다.
//Service단
@Transactional(rollbackFor = Exception.class)
@Override
public void insertTest(ADto aDto, BDto bDto) throws Exception {
int result = 0;
//첫번째 insert처리
result = testMapper.insertTest(aDto);
if(aResult==0) { throw new Exception("aDto를 insert하는데 실패함"); }
//두번째 insert처리
result = testMapper.insertTest(bDto);
if(bResult<=0) { throw new Exception("bDto를 insert하는데 실패함"); }
}
//Controller단
@PostMapping(value="insert/test")
public Map<String, Object> insertTest(ADto aDto, BDto bDto) {
Map<String, Object> responseData = new HashMap<>();
try {
testService.insertTest(aDto, bDto);
} catch (Exception e) {
responseData.put("result", "failure");
responseData.put("detail", e.getMessage());
return responseData;
}
responseData.put("result", "success");
return responseData;
}

'Framework > MyBatis' 카테고리의 다른 글
| Mapper 인터페이스를 사용한 프로젝트 (0) | 2023.04.24 |
|---|---|
| <selectKey> : insert + select를 한번에 (0) | 2023.04.20 |
| [03] SQL문 응용태그 : 동적 SQL, <association>, <collection> (0) | 2023.02.15 |
| [02] 페이징처리 : RowBounds (0) | 2023.02.14 |
| [01] Mybatis의 CRUD 방식 (0) | 2023.02.13 |
댓글