关于事务的概念不多介绍,在Spring中实现事务是在数据层和业务层保障一系列数据库操作同时成功或者同时失败。
Spring实现事务是通过Spring提供的接口:PlatformTransactionManger
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException; //事务提交
void rollback(TransactionStatus var1) throws TransactionException; //事务回滚
}
参考:Spring知识梳理
Spring注解开发实现事务:
(1)在业务层接口上添加Spring事务管理:
使用@Transactional注解
public interface AccountService {
/**
* 模拟转账操作
* @param out 传出方
* @param in 转入方
* @param money 金额
*/
//配置当前接口方法具有事务
@Transactional
public void transfer(String out,String in ,Double money) ;
}
(2)设置事务管理器
写一个返回值为PlatfornTransactionManager(面向接口编程)的方法,方法可写在JdbcConfig类当中,传入参数为DataSource实现引用类型的自动注入
//配置事务管理器,使用的是jdbc事务
@Bean //让Spring去加载这个Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
(3) 在Spring配置类上开启注解式事务驱动
@Configuration
@ComponentScan("xxxx.xxxxx")
@PropertySource("classpath:xxxx.properties")
@Import({xxxx.class,xxxxx.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}
这样,一个基础的Spring事务Demo就写完了
实现原理:
@Transactional注解会开启一个Spring事务,原本数据库的两个update操作同样也会各自开启一个事务,所以目前存在三个事务。Spring会做如下操作:在我的控制范围中所有的事务都要加入我开启的事务,Spring开启的事务被称为事务管理员,加入的事务成为事务协调员,此时程序中便只有一个事务。该事务中代码出现错误,整个事务就会回滚。
Spring是怎样实现介入dao操作的呢?因为在上面事务管理器中传入了一个DataSource对象,该对象要和实现数据增删改的DataSource是同一个对象。
事务的属性
这些属性都可以在@Transactional注解上开启
rollbackFor:Spring默认回滚的是Error和RunnTimeException 其他异常,编译时异常默认不进行回滚,有相关业务操作要手动加上该异常:
@Transactional(rollbackFor = {NullPointerException.class})
事务的传播行为
事务的传播行为:事务协调员对事务管理员所携带事务的处理态度(即事务管理员开启事务,事务协调员加入事务/不加入事务/重新开启事务)
配置事务的传播行为是一个枚举值
应用场景:如果有一个操作不想加入事务管理员,无论事务是否执行失败,该操作都要执行。例如:记录日志操作
这就要求日志开启的事务不要加入Spring开启的事务,此时就需要在日志操作方法上加上日志的传播行为参数:
public interface LogService {
//propagation设置事务属性:传播行为设置为当前操作需要新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
void log(String out, String in, Double money);
}
此时无论事务执行成功还是失败,都不影响日志的执行