| transactionManager | 사용할 Transaction manager 속성을 정의한 Transaction Manager의 id를 참조한다. Transaction Manager 속성 정의 내용은 하단의 내용을 참고하도록 한다. | ||
| transactionAttributes |
target의 메소드 별로 transaction attributes를 정의할 수 있다. 여기에 Propagation Behavior, Isolation Level, "readOnly" flag, Rollback 규칙를
설정할 수 있다.(하단 참고 그림을 참고한다)
e.g. key ="myMethod", value ="PROPAGATION_REQUIRED,readOnly"
|
||
| target | TransactionProxyFactoryBean bean의 abstract attribute 값을 true로 놓고, 이 TransactionProxyFactoryBean bean을 parent로 정의한 bean 내에 target을 정의한다. |

| PROPAGATION_MADATORY | 반드시 Transaction 내에서 메소드가 실행되야 하고,Transaction이 없는 경우에는 예외를 발생시킨다. |
| PROPAGATION_NESTED | Transaction에 있는 경우, 기존 Transaction 내의 nested transaction 형태로 메소드를 실행하고, nested transaction 자체적으로 commit, rollback이 가능하다. Transaction이 없는 경우, PROPAGATION_REQUIRED 속성으로 행동한다. |
| PROPAGATION_NEVER | Transaction 컨텍스트 없이 실행되어야 하며 Transaction이 있으면 예외를 발생시킨다. |
| PROPAGATION_NOT_SUPPORTED | Transaction 없이 메소드를 실행하며,기존의 Transaction이 있는 경우에는 이 Transaction을 호출된 메소드가 끝날 때까지 잠시 보류한다. |
| PROPAGATION_REQUIRED | Transaction 컨텍스트 내에서 메소드가 실행되어야 한다. 기존 Transaction이 있는 경우에는 기존 Transaction 내에서 실행하고, 기존 Transaction이 없는 경우에는 새로운 Transaction을 생성한다. |
| PROPAGATION_REQUIRED_NEW | 호출되는 메소드는 자신 만의 Transaction을 가지고 실행하고, 기존의 Transaction들은 보류된다. |
| PROPAGATION_SUPPORTS | 새로운 Transaction을 필요로 하지는 않지만, 기존의 Transaction이 있는 경우에는 Transaction 내에서 메소드를 실행한다. |
| ISOLATION_DEFAULT | 개별적인 PlatformTransactionManager를 위한 디폴트 격리 레벨 |
| ISOLATION_READ_COMMITTED | 이 격리수준을 사용하는 메소드는 commit 되지 않은 데이터를 읽을 수 없다. 쓰기 락은 다른 Transaction에 의해 이미 변경된 데이터는 얻을수 없다. 따라서 조회 중인 commit 되지 않은 데이터는 불가능하다. 대개의 데이터베이스에서의 디폴트로 지원하는 격리 수준이다. |
| ISOLATION_READ_UNCOMMITTED | 가장 낮은 Transaction 수준이다. 이 격리수준을 사용하는 메소드는 commit 되지 않은 데이터를 읽을 수 있다. 그러나 이 격리수준은 새로운 레코드가 추가되었는지 알수 없다. |
| ISOLATION_REPEATABLE_READ | ISOLATION_READ_COMMITED 보다는 다소 조금 더 엄격한 격리 수준이다. 이 격리 수준은 다른 Transaction이 새로운 데이터를 입력했다면, 새롭게 입력된 데이터를 조회할 수 있다는 것을 의미한다 |
| ISOLATION_SERIALIZABLE | 가장 높은 격리수준이다. 모든 Transaction(조회를 포함하여)은 각 라인이 실행될 때마다 기다려야 하기 때문에 매우 느리다. 이 격리수준을 사용하는 메소드는 데이터 상에 배타적 쓰기를 락을 얻음으로써 Transaction이 종료될 때까지 조회, 수정, 입력 데이터로부터 다른 Transaction의 처리를 막는다 가장 많은 비용이 들지만 신뢰할만한 격리 수준을 제공하는 것이 가능하다. |
<bean id="transactionSampleDataSource" parent="baseTransactionProxyDataSource">
<property name="target">
<bean class
="integration.anyframe.services.transaction.sample.TransactionTestSampleServiceImpl">
<property name="queryService" ref="queryService"></property>
</bean>
</property>
<property name="proxyInterfaces">
<list>
<value>
integration.anyframe.services.transaction.sample.TransactionTestSampleService
</value>
</list>
</property>
</bean>
<bean id="baseTransactionProxyDataSource"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManagerDataSource" />
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
public abstract class AbstractTransactionServiceTest extends AbstractTest {
public void testInsertRollback() throws Exception {
Thread.sleep(500);
// 1. get previous commit count and rollback count
int prevCommitCount = service.getCommitCount();
int prevRollbackCount = service.getRollbackCount();
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus txStatus = transactionManager.getTransaction(txDefinition);
try {
// 2. execute commit twice with a same data
TransactionVo vo = new TransactionVo();
vo.setCol1(Thread.currentThread().getName() + "-col1");
vo.setCol2(Thread.currentThread().getName() + "-col2");
vo.setCol3(new BigDecimal(System.currentTimeMillis()));
service.insertData(vo);
service.insertData(vo);
transactionManager.commit(txStatus);
} catch (QueryServiceException e) {
// 3. execute rollback
transactionManager.rollback(txStatus);
} finally {
// 4. assert
boolean checkedCommit = prevCommitCount == service.getCommitCount();
if (!checkedCommit)
throw new Exception("fail to get commit count.");
boolean checkedRollback = prevRollbackCount + 2 == service.getRollbackCount();
if (!checkedRollback)
throw new Exception("fail to get rollback count.");
}
}
중략...
DataSource Transaction 서비스는 DataSource를 사용하여 Local Transaction을 관리한다.
<bean id="transactionManagerDataSource"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref bean="common_datasource"/></property>
</bean>
<bean id="common_datasource
" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:/./db/sampledb"/>
<property name="username" value="sa"/>
</bean>
public class TransactionServiceTestDataSource extends AbstractTransactionServiceTest {
중략...
protected void setup() {
super.setup();
this.service = (TransactionTestSampleService)context.getBean("transactionSampleDataSource");
this.transactionManager = (PlatformTransactionManager)context
.getBean("transactionManagerDataSource");
}
}
Hibernate Transaction 서비스는 DataSource를 사용하여 Local Transaction과 Hibernate Session을 관리한다. HibernateTransactionManager는 LocalSessionFactoryBean 에 의존성을 가지고 있으므로 반드시 LocalSessionFactoryBean 설정과 함께 사용되어야 한다.
<bean id="transactionManagerHibernate" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="common_datasource" />
<property name="mappingDirectoryLocations">
<value>classpath:/integration/anyframe/services/hibernate/sample</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
</props>
</property>
</bean>
public class TransactionServiceTestHibernate extends AbstractTransactionServiceTest {
중략...
protected void setup() {
super.setup();
this.service = (TransactionTestSampleService)context.getBean("transactionSampleHibernate");
this.transactionManager = (PlatformTransactionManager)context
.getBean("transactionManagerHibernate");
}
}
JTA Transaction 서비스는 JTA를 사용한 Global Transaction 관리 부분을 추상화하여 해당 서비스가 JTA,JNDI 등에 종속적이지 않게 구현 가능 하도록 도와준다. JndiTemplate bean 설정과 함께 사용되어야 한다. 또한 이때 DataSource 서비스는 JNDI DataSource 서비스로 설정해줘야 한다.
<bean id="transactionManagerJTA"
class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="transactionManagerName" value="javax.transaction.TransactionManager" />
<property name="jndiTemplate" ref="jnditemplate"></property>
</bean>
<bean id="jnditemplate" class="org.springframework.jndi.JndiTemplate" >
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
<prop key="java.naming.provider.url">t3://107.108.150.108:7001</prop>
</props>
</property>
</bean>
<bean id="transactionManagerJTA"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
<property name="jndiTemplate" ref="jnditemplate"></property>
</bean>
<bean id="jnditemplate" class="org.springframework.jndi.JndiTemplate" >
<property name="environment">
<props>
<prop key="java.naming.factory.initial">jeus.jndi.JNSContextFactory</prop>
<prop key="java.naming.provider.url">107.108.150.108:9736</prop>
</props>
</property>
</bean>
<bean class="org.springframework.jndi.JndiObjectFactoryBean" id="common_datasource"> <property name="jndiName" value="STMRDS"/> <property name="jndiTemplate" ref="jnditemplate"/> </bean>
public class TransactionServiceTestHibernate extends AbstractTransactionServiceTest {
중략...
protected void setup() {
super.setup();
this.service = (TransactionTestSampleService)context.getBean("transactionSampleJTA");
this.transactionManager = (PlatformTransactionManager)context
.getBean("transactionManagerJTA");
}
}