스프링에서 트랜잭션 적용 시 트랜잭션 경계가 DAO 밖에 있고 범위가 넓으면 AOP를 이용하는 것이 편리하다. 하지만 Sql 레지스트리라는 제한된 오브젝트 내에서 간단한 트랜잭션이 필요한 경우라면 AOP와 같이 거창한 방법보다는 간단히 트랜잭션 추상화 API를 직접 사용하는게 편리할 것이다.
@Test
public void transactionalUpdate() {
checkFind("SQL1", "SQL2", "SQL3"); // 초기 상태를 확인한다.
Map<String, String> sqlmap = new HashMap<String, String>();
sqlmap.put("KEY1", "Modified1");
// 두 번째 SQL의 키를 존재하지 않는 것으로 지정한다.
// 이 때문에 테스트는 실패할 것이고 그때 과연 롤백이 일어났는지 테스트 한다.
sqlmap.put("KEY9999!@#$", "Modified9999");
try {
sqlRegistry.updateSql(sqlmap);
fail();
}catch (SqlUpdateFailureException e) {
}
// 첫번째 SQL은 정상적으로 수정했지만 트랜잭션이 롤백되기 때문에 다시 변경 이전 상태로 돌아와야 한다.
// 트랜잭션이 적용되지 않는다면 변경된 채로 남아서 테스트는 실패할 것이다.
checkFindResult("SQL1", "SQL2", "SQL3");
}
public class EmbeddedDbSqlRegistry implements UpdatableSqlRegistry {
SimpleJdbcTemplate jdbc;
// JdbcTemplate과 트랜잭션을 동기화해주는 트랜잭션 템플릿이다. 멀티스레드 환경에서 공유 가능하다.
TransactionTemplate transactionTemplate;
public void setDataSource(DataSource dataSource) {
jdbc = new SimpleJdbcTemplate(dataSource);
//DataSource로 TranscationManager를 만들고 이를 이용해 TranscationTemplate을 생성한다.
transactionTemplate = new TranscationTemplate(
new DataSourceTranscationManager(dataSource));
}
@Override
public void updateSql(final Map<String, String> sqlmap) throws SqlUpdateFailureException {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
// 트랜잭션 템플릿이 만드는 트랜잭션 경계 안에서 동작할 코드를 콜백 형태로 만들고
// execute() 메소드에 전달한다.
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
for (Map.Entry<String, String> entry : sqlmap.entrySet()) {
updateSql(entry.getKey(), entry.getValue());
}
}
});
}
}
@RunWith(SpringJunit4ClassRunner.class)
// xml -> java 설정 파일로
@ContextConfiguration(classes=TestApplicationContext.class)
public class UserDaoTest {
<bean>
의 전환<bean>
은 @Bean이 붙은 public 메소드로 만들어 주면 된다. 메소드 이름은 <bean>
의 id 값으로 한다.<bean id="dataSource" class="org.springboot.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="com.mysql.jdbc.mysql" />
...
</bean>
@Bean
public DataSource dataSource() {
SimpleDriverDataSource dataSource = new SimpleDriverDataSource ();
dataSource.setDriver(Driver.class);
dataSource.setUrl("jdbc:mysql://localhost/springbook?characterEncoding=UTF-8");
dataSource.setUsername("spring");
dataSource.setPassword("book");
return dataSource;
}
<bean id="transactionManager" class="org.springboot.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
@Bean
public PlatformTranscationManager transcationManager() {
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource());
return tm;
}