스프링 트랜잭션
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <aop:aspectj-autoproxy /> <bean id="testLogger" class="net.java_school.commons.TestLogger" /> <bean id="bankUi" class="net.java_school.bank.BankUi"> <property name="stream" value="#{T(System).out}" /> <property name="bank" ref="bank" /> </bean> <bean id="bank" class="net.java_school.bank.MyBank"> <property name="dao" ref="bankDao" /> </bean> <bean id="bankDao" class="net.java_school.bank.MyBankDao"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:XE" /> <property name="username" value="scott" /> <property name="password" value="tiger" /> </bean> </beans>
지금까지 입금과 출금 메소드는 void를 반환하도록 설계했기에 입금이나 출금이 성공 또는 실패했는지를 알 수 없었다. 입금과 출금시 dao가 정수값을 리턴하도록 자바 클래스를 수정한다.
BankDao.java
//입금 public int deposit(String accountNo, double amount); //출금 public int withdraw(String accountNo, double amount);
MyBankDao.java
@Override public int deposit(String accountNo, double amount) { Map<String, Object> params = new HashMap<String, Object>(); params.put("amount", amount); params.put("accountNo", accountNo); return getNamedParameterJdbcTemplate().update(DEPOSIT, params); } @Override public int withdraw(String accountNo, double amount) { Map<String, Object> params = new HashMap<String, Object>(); params.put("amount", amount); params.put("accountNo", accountNo); return getNamedParameterJdbcTemplate().update(WITHDRAW, params); }
다음과 같이 어노테이션을 사용해 트랜잭션을 설정한다.
MyBank.java
package net.java_school.bank; import java.util.List; import org.springframework.transaction.annotation.Transactional; public class MyBank implements Bank { //..중간 생략.. @Override @Transactional public void transfer(String from, String to, double amount) { try { int check = dao.withdraw(from, amount); if (check < 1) throw new RuntimeException("출금 실패"); check = dao.deposit(to, amount); if (check < 1) throw new RuntimeException("입금 실패"); } catch (Exception e) { throw new RuntimeException(e); } } //..중간 생략.. }
테스트하기 전에 BankUi.java의 메인 메소드를 아래를 참조해 수정한다.
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML //AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig
컴파일하고 실행한 후, 101 계좌에서 505 계좌(존재하는 않는 계좌)로 이체를 시도한다. 테스트에 성공했다면(즉, 이체가 취소되었다면), MyBank 클래스에서 transfer() 메소드 위에 있는 @Transactional을 제거하고 다시 이체를 시도한다.
JavaConfig 설정
BankConfig.java
package net.java_school.bank; import javax.sql.DataSource; import net.java_school.commons.TestLogger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableAspectJAutoProxy @EnableTransactionManagement public class BankConfig { @Bean public TestLogger testLogger() { return new TestLogger(); } @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@127.0.0.1:1521:XE"); dataSource.setUsername("scott"); dataSource.setPassword("tiger"); return dataSource; } @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } @Bean public BankDao bankDao() { MyBankDao bankDao = new MyBankDao(); bankDao.setDataSource(dataSource()); return bankDao; } @Bean public Bank bank() { Bank bank = new MyBank(); bank.setDao(bankDao()); return bank; } @Bean public BankUi bankUi() { BankUi ui = new BankUi(); ui.setBank(bank()); ui.setStream(System.out); return ui; } }
테스트하기 전에, applicationContext.xml 설정을 모두 주석 처리한다. BankUi.java의 메인 메소드를 아래를 참조해 수정한다.
//ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig