하지만 Exception 발생 여부와 상관 없이 로그를 남겨야 하는 경우라든지 메서드 내부에서 트랜잭션 안에서 실행되어야 하는 부분과 트랜잭션과 무관하게 실행되어야 하는 부분이 함께 존재한다면 애너테이션을 사용하여 처리할 수 없다. (사실은 Propagation 설정으로 가능할 것 같은데 잘 안 된다. 천천히 알아보기로 하구 일단..)
[PROPAGATION]
REQUIRED : 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, 신규 tx를 생성하고 실행
SUPPORTS : 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, 그냥 실행
MANDATORY : 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, Exception 발생
REQUIRES_NEW : 이미 tx가 존재할 경우, 해당 tx를 suspend 시키고 신규 tx를 생성 / tx가 없을 경우, 신규 tx를 생성
NOT_SUPPORTED : 이미 tx가 존재할 경우, 해당 tx를 suspend 시키고 tx 없이 실행 / tx가 없을 경우, 그냥 실행
NEVER : 이미 tx가 존재할 경우, Exception 발생 / tx가 없을 경우, tx 없이 실행
NESTED : 이미 tx가 존재할 경우, 해당 tx에 참여 / tx가 없을 경우, nested tx 실행
[ISOLATION]
DEFAULT : datastore에 의존
READ_UNCOMMITED : Dirty Reads, Non-Repeatable Reads, Phantom Reads 발생
READ_COMMITED : Dirty Reads 방지, Non-Repeatable Reads, Phantom Reads 발생
REPEATABLE_READ : Non-Repeatable Read 방지, Phantom Reads 발생
SERIALIZABLE : Phantom Read 방지
Transaction 처리 방식
- TransactionTemplate 사용
- TransactionManager 사용
dataSource 및 transactionManager 설정
spring 설정파일에 아래와 같이 설정한다.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://host:3306/databaseName?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="dbuser"/>
<property name="password" value="dbpw"/>
<property name="maxWait" value="28000"/>
<property name="maxIdle" value="100" />
<property name="minIdle" value="1" />
<property name="initialSize" value="10" />
<property name="timeBetweenEvictionRunsMillis" value="28000" />
<property name="minEvictableIdleTimeMillis" value="28000000" />
<property name="validationQuery" value="select 1 from dual"/>
<property name="testWhileIdle" value="true" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
TransactionTemplate 사용하기
서비스 클래스에 아래와 같이 프로퍼티를 설정한다.
@Resource(name="txManager")
protected DataSourceTransactionManager txManager;
protected TransactionTemplate transactionTemplate;
@PostConstruct
public void init() {
transactionTemplate = new TransactionTemplate(txManager);
}
@Override
public void myTrxMethod(final PutAccountInfoRequest request)
throws Exception {
// write log
MyTrxResult result = (MyTrxResult)
transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus status) {
boolean success = false;
Exception exception = null;
try {
// trx1
// trx2
} catch(Exception ex) {
exception = ex;
status.setRollbackOnly();
}
return new MyTrxResult();
}
});
// write transaction result
}
리턴되는 결과가 없는 경우에는 TransactionCallback 대신 TransactionCallbackWithoutResult 을 사용할 수도 있으나, TransactionCallback에서 null을 리턴해도 무방하다.
Transaction은 doInTransaction 메서드 단위로 처리가 되며, exception이 발생하였을 때 setRollbackOnly을 호출하면 메서드 내의 Transaction은 롤백처리된다.
TransactionManager 사용하기
protected TransactionStatus getTransaction() {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txManager.getTransaction(definition);
}
@Override
public void myTrxMethod(PutAccountInfoRequest request)
throws Exception {
// write log
boolean success = false;
Exception exception = null;
TransactionStatus txStatus = getTransaction();
try {
// trx1
// trx2
txManager.commit(txStatus);
} catch(Exception ex) {
exception = ex;
txManager.rollback(txStatus);
}
getTransaction하는 시점부터 commit 또는 rollback 할 때까지의 일련의 처리가 단일 트랜잭션으로 처리된다.
댓글 없음:
댓글 쓰기