在對數據庫進行操作時,有時候會把多個操作放到一個事務里,保證原子性,那么這個事務是怎么實現的呢?
下面我們先通過一個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的方法,不是代理方法,所以不會有事務的存在