1. 트랜잭션이란?
- 하나의 논리적인 작업 단위가 모두 성공했을 때 Commit, 실패했을 때 Rollback 하는 것
쉽게 계좌이체를 생각하자
A 출금 O -> B 입금 X => 말도 안됨
A 출금 X -> B 입금 O => 말도 안됨
2. 트랜잭션의 4가지 특징(ACID)
1) 원자성(Atomicity)
트랜잭션은 한 개 이상의 동작을 논리적으로 한 개의 작업 단위로 묶는다.
원자성은 트랜잭션 범위에 있는 모든 동작이 모두 실행되거나 또는 모두 실행이 취소됨을 보장한다.
모든 동작이 성공적으로 실행되면 트랜잭션 성공 후 커밋
하나라도 실패하면 트랙잭션은 실패하고 모든 과정을 롤백한다.
2) 일관성(Consistency)
트랜잭션이 종료되면, 시스템은 비즈니스에서 기대하는 상태가 된다.
ex) 구매 트랜잭션이 성공적으로 실행되면 결제 내역, 구매 내역, 재고 정보가 비즈니스에 맞게 저장되고 변경됨
3) 고립성(Isolation)
트랜잭션은 다른 트랜잭션과 독립적으로 실행되어야 하며, 서로 다른 트랜잭선이 동일한 데이터에 동시에 접근할 경우 알맞게 동시 접근을 제어해야 한다.
동시 접근 제어는 설정한 격리 레벨에 따라 달라진다.
4) 지속성(Durability)
트랜잭션이 완료되면, 그 결과는 지속적으로 유지되어야 한다.
현재의 어플리케이션이 변경되거나 없어지더라도 데이터는 유지된다.
일반적으로 물리적인 저장소를 통해서 트랜잭션 결과가 저장된다.
3. 스프링에서(JDBC나 MyBaits와 같이) 트랜잭션 처리를 하기 위해 DataSourceTransactionManager 인터페이스를 구현한 클래스를 트랜잭션 처리 관리자로 빈 객체 등록을 해야 한다.
JPA 트랜잭션 관리자는 JpaTransactionManager를
하이버네이트 트랜잭션 관리자는 HibernateTransactionManager를
JTA 트랜잭션 관리자는 JtaTransactionManager를 사용한다.
* 예제를 다루기 앞서 현재 dispatcher-servlet.xml과 dispatcher-service.xml은 부모-자식 계층으로 이루어진 상태.
[dispatcher-servlet.xml]
> 컴포넌트 스캔 기능 사용
[dispatcher-service.xml]
dispatcher-service.xml(공통 빈)에서 DB연동과 트랜잭션 처리를 할 수 있도록 transactionManager 빈 등록, NamedParameterJdbcTemplate 사용
4. 스프링의 트랜잭션 지원
1) 코드 기반의 트랜잭션 처리
- 개발자가 직접 코딩, 트랜잭션 매니저(TransactionManager), 트랜잭션 템플릿 클래스(TransactionTemplate) 이용
- 개발자가 직접 스프링의 트랜잭션을 처리할 수 있는 템플릿(스프링 트랜잭션 템플릿)을 사용해서 처리할 수 있음
2) 선언적 트랜잭션 처리
- TransactionTemplate과 달리 트랜잭션 처리를 코드에서 직접 수행하지 않고 설정 파일이나 애노테이션을 이용해서 트랜잭션의 범위, 롤백 규칙 등을 정의
ㄱ. xml 파일 설정
ㄴ. 애노테이션 기반의 트랜잭션 처리 : @Transactional 사용
[회원이 공지사항 글 INSERT + 회원의 point +1 UPDATE 트랜잭션 처리]
1) 코드 기반의 트랜잭션 처리 예제(TransactionTemplate 사용)
ㄱ. transactionManager 빈 등록 : 스프링 트랜잭션 처리 관리자
> 트랜잭션 처리도 DB 연동과 관련되어 있기 때문에 DataSource를 프로퍼티로 설정
ㄴ. 트랜잭션을 처리하는 템플릿 등록
> TransactionTemplate는 프로퍼티에 TransactionManager 빈을 지정
ㄷ. 트랜잭션 처리가 필요한 장소(ex. 메서드)에서 트랜잭션 처리
DAO(컨트롤러)에서 @Autowired 애노테이션으로 의존 주입
> 트랜잭션 템플릿을 사용하기 때문에 dispatcher-service.xml에서 생성한 빈객체 의존주입 필요
> TransactionCallbackWithoutResult 콜백 함수 사용 : 돌려주는 값이 없을 때 사용하는 트랜잭션 콜백 함수
- INSERT, UPDATE 등 콜백함수 안에서 처리를 하면 자동으로 모든 작업단위가 완료되면 커밋을 해주고 하나라도 실패하면 롤백을 해준다.
위의 메서드가 트랜잭션 처리가 안된 경우라면 아래와 같이 메서드가 구현되어 있을 것이다.
// [1] 트랜잭션 처리가 안된 경우
@Override
public void insertAndPointUpOfMember(Notice notice, String id) throws ClassNotFoundException, SQLException {
// 1. 새 공지사항 쓰기
String sql = "INSERT INTO "
+ "NOTICES(SEQ, TITLE, CONTENT, WRITER, REGDATE, HIT, FILESRC) "
+ "VALUES("
+ " (SELECT MAX(TO_NUMBER(SEQ))+1 FROM NOTICES), :title, :content, :writer, SYSDATE, 0, :filesrc)";
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(notice);
this.jdbcTemplate.update(sql, parameterSource);
// 2. point 1증가
sql = "UPDATE member "
+ "SET point = point + 1 "
+ "WHERE id = :id";
MapSqlParameterSource parameterSource2 = new MapSqlParameterSource();
parameterSource2.addValue("id", id);
this.jdbcTemplate.update(sql, parameterSource2);
} // insertAndPointUpOfMember
* 리턴하는 값이 있다면 TransactionCallback() 콜백 함수를 사용(교재 참고 정리)
TransactionTemplate의 execute() 메서드는 TransactionCallback 타입의 객체를 파리미터로 전달 받는다.
execute() 메서드는 내부적으로 TransactionManager를 이용해서 트랜잭션을 시작한 뒤에 파라미터로 전달받은 TransactionCallback의 doInTransaction() 메서드를 실행한다.
doInTransaction() 메서드가 정상적으로 실행되면, TransactionTemplate의 execute() 메서드는 트랜잭션을 커밋하고 결과를 리턴한다.
> TransactionTemplate의 트랜잭션 처리 과정
doInTransaction() 메서드는 throws를 통해서 발생시킬 수 있는 익셉션을 설정하고 있지 않기 때문에 doInTransaction() 메서드 내부에서는 런타임익셉션이나 에러타입의 익셉션만 발생시킬 수 있다.
만약, doInTransaction 내부에서 반드시 catch로 처리해주아야 하는 checked 익셉션을 발생시킨다면 doInTransaction() 메서드 내부에서 try-catch 블록을 사용해서 익셉션을 처리한 뒤 롤백 여부를 설정해 주어야 한다.
위 코드에서 작성한 catch 블록을 살펴보면 TransactionStatus의 setRollbackOnly() 메서드를 실행하고 있다. 이렇게 하면 TransactionTemplate은 트랜잭션을 롤백 시킨다.
doInTransaction 내부에서 발생한 익셉션 객체를 리턴했는데 이렇게 한 이유는 doInTransaction() 메서드 내부에서 발생한 익셉션을 외부에서 사용할 수 있도록 하기 위함이다.
2-1) 선언적 트랜잭션 처리 : xml 기반 예제
(1) xml 설정 파일에서 <tx:advice>와 <aop:config> 태그를 이용하여 트랜잭션 처리
> tx와 aop를 사용하기 위해서 dispatcher-servlet.xml beans 태그에 설정 필요
(2) dispatcher-servlet.xml에서 <tx:advice>와 <aop:config> 태그 설정
ㄱ.
<tx:advice> 태그로 transaction manager를 설정 후 이름(id) 설정
<tx:method> 태그에서 name 속성으로 트랜잭션 처리를 할 메서드의 이름을 설정
isolation, propagation, read-only, timeout은 아래에 보이는 값이 기본 값이므로 설정을 해주지 않아도 된다.
ㄴ.
<aop:config> 태그 안에서 <aop:pointcut> 태그로 expression 속성에 execution으로 포인트컷을 설정
(within을 사용한 부분도 execution으로 설정한 것과 동일한 것이기 때문에 지우거나 주석처리해도 상관없음 within 명시자가 있다는 것을 보여주는 용)
<aop:advisor> 태그로 advice-ref 속성으로 tx:advice의 id를 설정해주고 pointcut-ref 속성으로 <aop:pointcut> 태그로 지정한 포인트컷의 id 값으로 설정
코드 기반의 트랜잭션 처리에서 코딩했던 콜백함수는 다 지우고 아래와 같이 메서드 구현
2-2) 선언적 트랜잭션 처리 : @Transactional 애노테이션을 이용한 설정 예제
(1) @Transactional 애노테이션을 트랜잭션 처리가 필요한 메서드에 선언
(2) dispatcher-servlet.xml에 애노테이션 사용을 위해
<tx:annotation-driven transaction-manager="transactionManager"/> 추가
* 인터페이스 안에서 @Transactional 애노테이션을 선언해도 트랜잭션 처리가 필요한 메서드로 사용 가능
어떤 클래스 안에 있는 모든 메서드가 트랜잭션이 필요하다면 클래스 위에 @Transactional 애노테이션 선언
트랜잭션과 DataSource - p519 참고
'TIL > Spring' 카테고리의 다른 글
[SIST] Spring_days07_스프링 트랜잭션 격리 레벨(Isolation) (0) | 2022.07.19 |
---|---|
[SIST] Spring_days07_스프링 트랜잭션 전파방식(propagation) (0) | 2022.07.19 |
[SIST] Spring_days06_Spring JDBC(NamedParameterJdbcTemplate) / Spring 컨테이너의 추가 설명(WebApplicationContext 부모-자식 계층) (0) | 2022.07.18 |
[SIST] Spring_days05_Spring의 DB 연동(Spring JDBC) / JdbcTemplate (0) | 2022.07.17 |
[SIST] Spring_days05_Spring 파일 업로드 (0) | 2022.07.16 |