这样的方式对于一个庞大的方法可以将其中一部分事务化,定义事务开始和结束的位置。
这是Spring推荐使用的方法。
首先向AccountServiceImpl类中添加一个TransactionTemplate属性及其setter方法,然后在tansfertMoney方法中调用TransactionTemplate.execute方法,以TransactionCallbackWithoutResult类创建的匿名类为输入参数。
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transferMoney(final long sourceAccountId, final long targetAccountId,
final double amount) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
}
});
}
}
然后在Configuration类中通过将transactionManager注入其构造函数来定义transactionTemplate Bean。tansactionManager和dataSource Bean定义不变。并将tansactionTemplate Bean注入accountService Bean中。
@Configuration
@Import(Ch4Configuration.class)
public class Ch6Configuration {
@Bean
public TransactionTemplate transactionTemplate() {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(transactionManager());
return transactionTemplate;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
@Bean
@Autowired
public AccountService accountService(AccountDao accountDao) {
AccountServiceImpl bean = new AccountServiceImpl();
bean.setAccountDao(accountDao);
bean.setTransactionTemplate(transactionTemplate());
return bean;
}
}
然后就可以在Main类中测试新的实现了。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
Ch6Configuration.class);
AccountService accountService = applicationContext.getBean(AccountService.class);
accountService.transferMoney(100L, 101L, 5.0d);
}
}
这是一种低级的方法,与JDBC API管理事务边界类似。
首先向AccountServiceImpl类中添加一个PlatformTransactionManagerAPI属性及其setter方法。用该PlatformTransactionManager创建新的TransactionDefinition对象以便获取TransactionStatus。用AccountDao方法完成数据访问操作,顺利则通过transactionManager.commit(status)提交事务,否则使用transactionManager.rollback(status)确定是否回滚。
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private PlatformTransactionManager transactionManager;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void transferMoney(long sourceAccountId, long targetAccountId,
double amount) {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
Account sourceAccount = accountDao.find(sourceAccountId);
Account targetAccount = accountDao.find(targetAccountId);
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
accountDao.update(sourceAccount);
accountDao.update(targetAccount);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new RuntimeException(e);
}
}
}
在配置类中将transactionManager注入accountService Bean中。
@Configuration
@Import(Ch4Configuration.class)
public class Ch6Configuration {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
@Bean
@Autowired
public AccountService accountService(AccountDao accountDao) {
AccountServiceImpl bean = new AccountServiceImpl();
bean.setAccountDao(accountDao);
bean.setTransactionManager(transactionManager());
return bean;
}
}
然后就可以运行Main函数测试新的实现了。
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
Ch6Configuration.class);
AccountService accountService = applicationContext.getBean(AccountService.class);
accountService.transferMoney(101L, 100L, 5.0d);
}
}
将事务功能作为一个AOP通知,使用Spring AOP功能进行处理。
可使用MethodInceptor拦截方法调用,在调用前后执行一些额外操作。并在XML文件中的aop:config元素中使用aop:advisor定义在哪些方法上触发该逻辑。
使用回调机制指定当前事务结束时需要执行的自定义代码块。TransactionSynchronization接口定义为:
public interface TransactionSynchronization extends Flushable{
int STATUS_COMMITED = 0;
int STATUS_ROLLED_BACK = 1;
int STATUS_UNKNOWN = 2;
void beforeCommit(boolean readOnly);
void afterCommit();
void afterCompletion(int status);
}
提交事务前调用befroeCommit方法,但不确定调用方法后会提交当前事务。该方法中抛出的任何异常都会传递给调用这。