核心接口
1️⃣PlatformTransactionManager
即平台事务管理器,依赖于数据源,可以保证拿到的是同一个connection
有三个api:
- getTransaction:通过事务定义获得事务状态
- commit:提交事务
- rollback:回滚事务
2️⃣TransactionStatus
记录事务状态
3️⃣TransactionDefinition
事务的名称、事务的隔离级别、事务的传播行为、事务的只读状态、事务的超时时间
三者之间的关系如图:
传播行为
实现多个事务的共享,当方法之间存在调用关系时候,应该如何提交事务,发生异常时是否回滚
例如:methodB调用到了methodA
methodA(){
//method1里的内容
}
methodB(){
//method2里的内容
methodA();
}
有三种常用的传播行为
1️⃣REQUIRED(默认值)
如果没有事务就创建一个,如果包含了事务,则加入进来 → 作为一个事务
一荣俱荣、一损俱损:要么一起提交,要么一起回滚
在上述例子中:
如果methodB的代码发生异常:AB都回滚
如果methodA的代码发生异常:AB都回滚
2️⃣REQUIRES_NEW
如果没有事务就新建一个,如果包含事务,则以一个新事务的形式存在
自私型事务:别人不能影响我,但是我可以影响别人;外围不能影响内部,内部可以影响外围
上述例子中:
如果methodB的代码发生异常:B回滚、A不回滚
如果methodA的代码发生异常:A回滚、B也回滚
3️⃣NESTED
如果没有事务就新建一个,如果包含事务,则以嵌套事务的方式运行
无私型:外围会影响内部,内部不会影响外围。
上述例子中:
如果methodB的代码发生异常:B回滚、A也回滚
如果methodA的代码发生异常:A回滚、B不回滚
使用事务
使用事务实际上就是给特定的方法(代码)做一个“事务”的增强。
例如,现在有个转账业务:
根据两个人的id,和要转的钱,实施转账业务
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
AccountMapper accountMapper;
@Override
public void transfer(Integer fromId, Integer destId, Integer money) {
Integer fromMoney = accountMapper.selectMoneyById(fromId) - money;
Integer destMoney = accountMapper.selectMoneyById(destId) + money;
accountMapper.updateMoneyById(fromMoney, fromId);
//int i = 1/0;
accountMapper.updateMoneyById(destMoney, destId);
}
}
上述如果不做处理,发生异常时,就可能发生转账者钱扣了,但是收款者钱没有增加得情况。事务增强可以避免这种情况。
事务增强的方式有多种:
都需要TransactionManager:
<!--TransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
ref就是dataSource组件的id,见文章:dataSource
1️⃣TransationTemplate(了解)
第一步
配置:
<!--TransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!--TransactionTemplate-->
<bean class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
第二步
在 transactionTemplate上调用execute方法,哪部分需要增加事务,就把哪部分丢到execute方法方法中
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
AccountMapper accountMapper;
@Autowired
TransactionTemplate transactionTemplate;
@Override
public void transfer(Integer fromId, Integer destId, Integer money) {
Integer fromMoney = accountMapper.selectMoneyById(fromId) - money;
Integer destMoney = accountMapper.selectMoneyById(destId) + money;
/*Object execute = transactionTemplate.execute(new TransactionCallback<Object>() {
//哪一部分需要增加事务,哪部分代码就放入到doInTransaction方法中
//doInTransaction方法的返回值 → 给到TransactionTemplate的execute方法
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
accountMapper.updateMoneyById(fromMoney, fromId);
//int i = 1 / 0;
accountMapper.updateMoneyById(destMoney, destId);
return 5;
}
});
System.out.println(execute);*/
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountMapper.updateMoneyById(fromMoney, fromId);
int i = 1/0;
accountMapper.updateMoneyById(destMoney, destId);
}
});
}
}
2️⃣TransactionProxy
与SpringAOP类似,通过委托类组件生成一个事务代理组件
第一步
配置:
<!--TransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!--委托类组件accountServiceImpl-->
<!--代理组件-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="accountServiceImpl"/>
<property name="transactionManager" ref="transactionManager"/>
<!--TransactionAttributes → TransactionDefinition-->
<property name="transactionAttributes">
<!--properties类型:既要表达key,又要表达value-->
<props>
<!--
key的含义:方法名
value的含义:TransactionDefinition
PROPAGATION_XXX:传播行为
ISOLATION_XXX:隔离级别
readOnly:只读
timeout_数字:超时时间,单位是秒
-Exception:当发生XXX异常的时候回滚
+Exception:当发生xxx异常是不回滚
-->
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_5</prop>
</props>
</property>
</bean>
配置完后会在Spring容器中生成一个accountServiceImpl的代理类,id为刚刚在配置中写的“accountServiceProxy”
配置完就可以直接使用代理类了
但使用此方法会给委托类的所有方法加上事务增强
3️⃣Advisor
可以给指定范围内的方法加上事务增强
第一步
配置:
注意需要引入tx的schema
<!--TransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com..service..*(..))"/>
<!--事务通知组件-->
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!--tx:advice-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<!--method definition-->
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false" timeout="5"/>
</tx:attributes>
</tx:advice>
4️⃣Annotation-driven(重点)
精准打击,指哪打哪——注解在哪个方法上,哪个方法就加上事务增强
第一步
配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
第二步
在方法上加注解
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
AccountMapper accountMapper;
@Override
@Transactional
public void transfer(Integer fromId, Integer destId, Integer money) {
Integer fromMoney = accountMapper.selectMoneyById(fromId) - money;
Integer destMoney = accountMapper.selectMoneyById(destId) + money;
accountMapper.updateMoneyById(fromMoney, fromId);
//int i = 1/0;
accountMapper.updateMoneyById(destMoney, destId);
}
}
也可以加在类上,意味着当前组件的全部方法都加上事务增强
也可以通过给@Transactional注解的一些属性赋值来指定事务增强
如:
@Transactional(isolation = Isolation.REPEATABLE_READ,
propagation = Propagation.REQUIRED,
readOnly = true,
timeout = 5)