Spring事务操作源码解析

闲来无事,重新看了下spring事务管理源码,写个笔记。

 

1、TransactionTemplate

当需要在事务中执行一个DB操作时,执行:

 

 
  1. transactionTemplate.execute(new TransactionCallback<MyDO>{

  2. public MyDO doInTransaction(TransactionStatus status){

  3. myDao.update();

  4. ...;

  5. myDao.insert();

  6. }

  7. });


事务中任何操作抛出异常,整个事务就会回滚掉,不用应用代码显式处理。

 

 

 
  1. public <T> T execute(TransactionCallback<T> action) throws TransactionException {

  2. if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {

  3. return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);

  4. }

  5. else {

  6. TransactionStatus status = this.transactionManager.getTransaction(this);//这里会根据当前事务状态确定是否开启新事务

  7. T result;

  8. try {

  9. result = action.doInTransaction(status);

  10. }

  11. catch (RuntimeException ex) {

  12. // Transactional code threw application exception -> rollback

  13. rollbackOnException(status, ex);

  14. throw ex;

  15. }

  16. catch (Error err) {

  17. // Transactional code threw error -> rollback

  18. rollbackOnException(status, err);

  19. throw err;

  20. }

  21. catch (Exception ex) {

  22. // Transactional code threw unexpected exception -> rollback

  23. rollbackOnException(status, ex);

  24. throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");

  25. }

  26. this.transactionManager.commit(status);

  27. return result;

  28. }

  29. }


接下来是如何开启事务的?看transactionManager.getTransaction(this)

 

 

2、DataSourceTransactionManager

TransactionTemplate中包含一个dataSourceTransactionManager变量,用来管理事务,一切都是由他关联起来的。

 

 
  1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {

  2. Object transaction = doGetTransaction();

  3. if (isExistingTransaction(transaction)) {//已开启事务,就返回当前事务状态

  4. // Existing transaction found -> check propagation behavior to find out how to behave.

  5. return handleExistingTransaction(definition, transaction, debugEnabled);

  6. }

  7. ...

  8. try {

  9. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);

  10. DefaultTransactionStatus status = newTransactionStatus(

  11. definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

  12. doBegin(transaction, definition);//这里就是开启事务啦!

  13. prepareSynchronization(status, definition);

  14. return status;

  15. }

  16. }


3、来看下如何doBegin的:

 

 

 
  1. protected void doBegin(Object transaction, TransactionDefinition definition) {

  2. DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

  3. Connection con = null;

  4.  
  5. try {

  6. if (txObject.getConnectionHolder() == null ||

  7. txObject.getConnectionHolder().isSynchronizedWithTransaction()) {

  8. Connection newCon = this.dataSource.getConnection();//看到没?这个就是db连接了~

  9. txObject.setConnectionHolder(new ConnectionHolder(newCon), true);//放到连接holder里面

  10. }

  11.  
  12. // 这里绑定db连接到当前线程,哦,原来是依靠threadlocal实现的!注意,这里的key是Datasource

  13. if (txObject.isNewConnectionHolder()) {

  14. TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());

  15. }

  16. }

  17. ...

  18. }


现在事务开启了,链接也放在线程变量里面了,那如何跟dao操作关联起来呢?这里以ibatis为例分析下。

 

4、从SqlMapClient初始化看起:

 

 
  1. <bean id="mySqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">

  2. <property name="dataSource" ref="myDataSource"/>

  3. <property name="configLocations">

  4. <list>

  5. <value>classpath:sqlmap/sqlmap-my.xml</value>

  6. </list>

  7. </property>

  8. </bean>


这个dataSource跟事务管理器的dataSource是同一个。看org.springframework.orm.ibatis.SqlMapClientFactoryBean初始化后:

 

 

 
  1. public void afterPropertiesSet() throws Exception {

  2. try {

  3. ...

  4. // Tell the SqlMapClient to use the given DataSource, if any.

  5. if (this.dataSource != null) {

  6. TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();

  7. DataSource dataSourceToUse = this.dataSource;

  8. if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {

  9. dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);//搞了一个数据源代理

  10. }

  11. transactionConfig.setDataSource(dataSourceToUse);

  12. ...

  13. }

  14. }

  15. }


如果没猜错,spring在ibatis从这个代理数据源获取连接时候做了拦截处理,如果当前线程已经开启了事务,就直接用这个事务关联的连接了,看源码:

 

 

 
  1. public TransactionAwareDataSourceProxy(DataSource targetDataSource) {

  2. super(targetDataSource);

  3. }


5、继续看怎么取连接的:

 

 

 
  1. public Connection getConnection() throws SQLException {

  2. DataSource ds = getTargetDataSource();

  3. Assert.state(ds != null, "'targetDataSource' is required");

  4. return getTransactionAwareConnectionProxy(ds);//连接也包装成了代理对象,看下这个代理的连接是怎么从datasource搞出来的

  5. }

 

 
  1. protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) {

  2. return (Connection) Proxy.newProxyInstance(

  3. ConnectionProxy.class.getClassLoader(),

  4. new Class[] {ConnectionProxy.class},

  5. new TransactionAwareInvocationHandler(targetDataSource));

  6. }

  7.  
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  9. ...

  10. Connection actualTarget = this.target;

  11. if (actualTarget == null) {

  12. actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);//怎么取的呢?

  13. }

  14. //真正用这个连接的时候返回原始对象

  15. if (method.getName().equals("getTargetConnection")) {

  16. // Handle getTargetConnection method: return underlying Connection.

  17. return actualTarget;

  18. }

  19. ...

  20. }


6、看下DataSourceUtils的doGetConnection这个方法:

 

 

 
  1. public static Connection doGetConnection(DataSource dataSource) throws SQLException {

  2. //从当前线程上下文取出连接holder,这个就是开启事务时候放进去的,还记得不?

  3. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

  4. if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {

  5. conHolder.requested();

  6. if (!conHolder.hasConnection()) {

  7. logger.debug("Fetching resumed JDBC Connection from DataSource");

  8. conHolder.setConnection(dataSource.getConnection());

  9. }

  10. return conHolder.getConnection();

  11. }

  12. ...

  13. }


好了,总结一下:
1)TransactionTemplate.execute执行事务块时,DataSourceTransactionManager开启事务,并把db连接用事务同步器TransactionSynchronizationManager注册到线程变量中中。
2)事务块中实行dao方法时,sqlMapClient从数据源获取连接是通过代理数据源取到了代理连接,在操作db的时候,这个代理连接返回了当前线程上下文中的连接。这样,同一个事务块中的多个dao操作就公用了同一个db连接,实现了事务悄无声息的封装。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值