Spring事务之如何保证同一个Connection对象
来源:互联网 发布:java做界面的工具 编辑:程序博客网 时间:2024/06/05 11:07
一、传统JDBC事务管理
首先我们先看一下,jdbc的事务配置是在Connection对消里面有对应的方法,比如setAutoCommit,commit,rollback这些方法就是对事务的操作。
conn.setAutoCommit(false);//设置事务非自动提交conn.commit();//提交事务conn.rollback();//事务回滚
这样必然存在一些问题,如:把业务逻辑代码和事务处理代码混合起来,同时存在代码重复性。如下是一段典型的控制事务代码:
private DataSource dataSource = null; public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } public void update() { Connection conn = null; PreparedStatement pstmt = null; try { conn = dataSource.getConnection(); conn.setAutoCommit(false);//设置事务非自动提交 String sql = "update testTable set name='测试数据' where id = '1'"; pstmt = conn.prepareStatement(sql); pstmt.execute(); conn.commit();//提交事务 } catch (Exception e) { try { conn.rollback();//事务回滚 } catch (Exception e1) { e1.printStackTrace(); } e.printStackTrace(); } finally { try { if(pstmt!=null) pstmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
二、Spring中的事务原理
Spring容器的事务机制的实质是对传统JDBC的封装,也即是Spring事务管理无论是对单数据库实例还是分布式数据库实例,要实现事务管理,那么必须保证在一个事务过程获得Connetion对象是同一个,那么即使在同一个函数中调用其他多个的函数,通过Spring框架的AOP动态代理机制,使得Spring容器底层能够按传统JDBC的方式进行事务处理,从而保证对这个函数做事务控制。
Spring框架具有支持多数据源的特性,在获得数据库Connection对象往往是通过DataSource中获得,DataSource这个接口往往由不同的厂商驱动实现,因此Spring框架往往是对DataSource进一步的封装保证每次获得的Connection为相同的,这就保证了一个业务方法里面进行多次dao操作,调用的都是一个connection对象,同时保证了多个dao都是在一个事务里面。
package javax.sql;import java.sql.Connection;import java.sql.SQLException;import java.sql.Wrapper;/** * <p>A factory for connections to the physical data source that this * <code>DataSource</code> object represents. An alternative to the * <code>DriverManager</code> facility, a <code>DataSource</code> object * is the preferred means of getting a connection. An object that implements * the <code>DataSource</code> interface will typically be * registered with a naming service based on the * Java<sup><font size=-2>TM</font></sup> Naming and Directory (JNDI) API. * <P> * The <code>DataSource</code> interface is implemented by a driver vendor. * There are three types of implementations: * <OL> * <LI>Basic implementation -- produces a standard <code>Connection</code> * object * <LI>Connection pooling implementation -- produces a <code>Connection</code> * object that will automatically participate in connection pooling. This * implementation works with a middle-tier connection pooling manager. * <LI>Distributed transaction implementation -- produces a * <code>Connection</code> object that may be used for distributed * transactions and almost always participates in connection pooling. * This implementation works with a middle-tier * transaction manager and almost always with a connection * pooling manager. * </OL> * <P> * A <code>DataSource</code> object has properties that can be modified * when necessary. For example, if the data source is moved to a different * server, the property for the server can be changed. The benefit is that * because the data source's properties can be changed, any code accessing * that data source does not need to be changed. * <P> * A driver that is accessed via a <code>DataSource</code> object does not * register itself with the <code>DriverManager</code>. Rather, a * <code>DataSource</code> object is retrieved though a lookup operation * and then used to create a <code>Connection</code> object. With a basic * implementation, the connection obtained through a <code>DataSource</code> * object is identical to a connection obtained through the * <code>DriverManager</code> facility. * * @since 1.4 */public interface DataSource extends CommonDataSource,Wrapper { /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. * * @return a connection to the data source * @exception SQLException if a database access error occurs */ Connection getConnection() throws SQLException; /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. * * @param username the database user on whose behalf the connection is * being made * @param password the user's password * @return a connection to the data source * @exception SQLException if a database access error occurs * @since 1.4 */ Connection getConnection(String username, String password) throws SQLException;}
三、Spring中事务处理过程
首先我们先看JdbcTemplate类数据访问类
public Object execute(ConnectionCallback action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); try { Connection conToUse = con; if (this.nativeJdbcExtractor != null) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } else { conToUse = createConnectionProxy(con); } localObject1 = action.doInConnection(conToUse); } catch (SQLException ex) { Object localObject1; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex); } finally { DataSourceUtils.releaseConnection(con, getDataSource()); } }
由上述源码中Connection con = DataSourceUtils.getConnection(getDataSource());这个可以看出,DataSourceUtils类保证当前线程获得的是同一个Connection对象。下面我们主要分析DataSourceUtils类:
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); } catch (SQLException ex) { } throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource); if ((conHolder != null) && ((conHolder.hasConnection()) || (conHolder.isSynchronizedWithTransaction()))) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { logger.debug("Registering transaction synchronization for JDBC Connection"); ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; }
由以上源码可以知道,数据库连接从TransactionSynchronizationManager中获得,如果已经存在则获得,否则重新从DataSource创建一个连接,并把这个连接封装为ConnectionHolder,然后注册绑定到TransactionSynchronizationManager中,并返回Connection对象。同时,可以看出DataSource和ConnectionHolder的存储管理在TransactionSynchronizationManager中,继续分析TransactionSynchronizationManager中的关键代码:
private static final ThreadLocal resources = new NamedThreadLocal("Transactional resources"); public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Object value = doGetResource(actualKey); if ((value != null) && (logger.isTraceEnabled())) { logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } return value; } private static Object doGetResource(Object actualKey) { Map map = (Map)resources.get(); if (map == null) { return null; } Object value = map.get(actualKey); if (((value instanceof ResourceHolder)) && (((ResourceHolder)value).isVoid())) { map.remove(actualKey); value = null; } return value; } public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map map = (Map)resources.get(); if (map == null) { map = new HashMap(); resources.set(map); } if (map.put(actualKey, value) != null) { throw new IllegalStateException("Already value [" + map.get(actualKey) + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); }
分析源码可以得出,
(1)TransactionSynchronizationManager内部用ThreadLocal对象存储资源,ThreadLocal存储的为DataSource生成的actualKey为key值和ConnectionHolder作为value值封装成的Map。
(2)结合DataSourceUtils的doGetConnection函数和TransactionSynchronizationManager的bindResource函数可知:在某个线程第一次调用时候,封装Map资源为:key值为DataSource生成actualKey【Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);】value值为DataSource获得的Connection对象封装后的ConnectionHolder。等这个线程下一次再次访问中就能保证使用的是第一次创建的ConnectionHolder中的Connection对象。
当事务结束后,调用【DataSourceUtils.releaseConnection(con, getDataSource());】将ConnectionHolder从TransactionSynchronizationManager中解除。当谁都不用,这个连接被close。
public static void releaseConnection(Connection con, DataSource dataSource) { try { doReleaseConnection(con, dataSource); } catch (SQLException ex) { logger.debug("Could not close JDBC Connection", ex); } catch (Throwable ex) { logger.debug("Unexpected exception on closing JDBC Connection", ex); } } public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException { if (con == null) { return; } if (dataSource != null) { ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource); if ((conHolder != null) && (connectionEquals(conHolder, con))) { conHolder.released(); return; } } if ((!(dataSource instanceof SmartDataSource)) || (((SmartDataSource)dataSource).shouldClose(con))) { logger.debug("Returning JDBC Connection to DataSource"); con.close(); } }
- Spring事务之如何保证同一个Connection对象
- Spring事务之如何保证同一个Connection对象
- 实现一个事务多个dao情况下,使用同一个connection,保证线程安全
- 如何保证多个对象操作的是同一个资源?
- spring事务 connection
- 如何保证数据库读写事务
- 如何保证数据库读写事务
- spring+mybatis 事务之如何在service层配置事务
- 浅析spring中创建的对象如何保证不被回收
- JDBC之Transaction(事务),SetAutoCommit保证事务原子性
- spring如何管理事务
- 【Java基础】采用ThreadLocal封装Connection控制事务,保证线程安全
- spring笔记之事务
- Spring之配置事务
- Spring之事务
- spring事务之REQUIRED
- Spring之事务控制
- spring事务和对象锁
- 阿里系技术博客原力觉醒,首批36支梦之队亮相云栖社区!
- QT读取access是数据库表名中文乱码
- Servlet写验证码
- NYOJ1112求次数
- Java中的数组
- Spring事务之如何保证同一个Connection对象
- css的定位:
- 【Android】Mainifest文件手动添加Activity
- c++上机实验3定期存款利息计算器
- UIWebView的加载本地数据的三种方式
- Windows Dev Intro - Register Dll
- 第七周项目3 角色类组合多文件
- python笔记(五)
- sublime text3的一些插件(持续更新中)