在對數據庫進行操作時,有時候會把多個操作放到一個事務里,保證原子性,那么這個事務是怎么實現的呢?
下面我們先通過一個demo看一下事務的使用:
一:事務的使用
數據庫jdbc配置:
##數據源配置 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/study jdbc.username=root jdbc.password=root
spring-context.xml配置:
<!--加載properties配置文件-->
<context:property-placeholder location="classpath*:*.properties" />
<!-- 配置數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- 指定數據源和配置文件路徑 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自動掃描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:UserMapper.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hello.mapper"></property>
</bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven/>
寫一個類,方法上加上@Transactional注解
@Service
public class TestTransactionalService {
@Autowired
private UserMapper userMapper;
@Transactional
public int add(){
User user = new User();
user.setName("Lucy");
user.setAge("30");
int effectCount = userMapper.insert(user);
System.out.println("影響條數: "+effectCount);
int i = 10/0;
return effectCount;
}
}
測試代碼:

運行結果: 從結果可以看到影響條數為1,但是數據庫沒有數據,可以看出事務回滾生效了
二:事務的原理分析
1:注冊埋點,注冊BeanDefinition對象,實例化BeanPostProcessor對象,並注冊到beanPostProcessors中
配置開啟事務管理:

通過命名空間找到對象的handler類:

注冊自定義標簽tx的屬性解析器,AnnotationDrivenBeanDefinitionParser







AutoProxyCreator已經以BeanDefinition的形式注冊到了BeanDefinitionMaps中。
注冊其他類:



2:實例化並注冊到beanPostProcessors中
是在refresh方法的registryBeanPostProcessor中實現的,這里不再贅述
3:尋找增強,創建代理






用增強去匹配當前bean信息:






創建代理:

4:攔截方法,對方法進行增強

TestTransactionalService這個對象已經被代理
進入DynamicAdvisedInterceptor類的intercept方法:

只有一個增強:

TransactionInterceptor 的 invoke方法


拋出異常,進入catch代碼塊:

回滾事務:



DataSourceTransactionManager類的doRollback回滾方法:

可以看出正在執行回滾操作是在DataSourceTransactionManager類中進行的

下面把導致異常的代碼去掉,看一下提交的情況:

提交事務:




調到了DataSourceTransactionManager的doCommit事務:

到這里事務管理的分析就結束了,實現原理就是對數據庫的操作方法進行增強,如果執行成功就commit,執行失敗就rollback。
a: 看一下異常回滾里面小細節:rollbackFor ,什么異常才回滾?

如果@Transactional注解上不配置rollbackFor 默認是RuntimException 或者 Error

如果自定義rollbackFor類型:

b:如果@Transactional注釋的方法不是public修飾的,也不會被代理,找不到@Transactional注解的信息


c:如果是內部調用,會不會生效,從其他類調用add方法:

調用add方法,進入intercept


在add方法中調用addData,實際是調用target的方法,不是代理方法,所以不會有事務的存在

