하지만 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 할 때까지의 일련의 처리가 단일 트랜잭션으로 처리된다.
댓글 없음:
댓글 쓰기