1.JdbcTemplate
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();//首先从当前的线程上下文中获取 } //Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource");
public Object execute(StatementCallback action) throwsDataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con =DataSourceUtils.getConnection(getDataSource()); Statement stmt = null; try{ Connection conToUse =con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } stmt =conToUse.createStatement(); applyStatementSettings(stmt); Statement stmtToUse =stmt; if (this.nativeJdbcExtractor != null) { stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt); } Object result =action.doInStatement(stmtToUse); SQLWarning warning =stmt.getWarnings(); throwExceptionOnWarningIfNotIgnoringWarnings(warning); return result;//返回语句执行的结果 } catch(SQLException ex) { //Release Connection early, to avoid potential connection pool deadlock //in the case when the exception translator hasn't been initialized yet. JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex); } finally{ JdbcUtils.closeStatement(stmt);//最后释放资源关闭连接 DataSourceUtils.releaseConnection(con, getDataSource()); } }
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
然后定义一个:DefaultTransactionDefinition
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
由此创建一个TransactionStatus:
privateTransactionStatus beginTransaction(){ DefaultTransactionDefinition def = newDefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); returngetMager().getTransaction(def); } privateDataSourceTransactionManager getMager(){ DataSourceTransactionManager man= (DataSourceTransactionManager) con.getBean("transactionManager"); returnman; }
下面看创建status时做了什么?
connectionholder字面意思为连接持有者,即为每个线程保存绑定连接的对象,每个线程绑定的连接保存在该对象中,然后保存在一个map里面,key为当前的DataSource,该DataSource为应用程序级的,可以是一个tns连接描述,也可以是一个jndi也可以是一个连接池对象,总之所有线程共享,通过该key值找到线程自己通过该DateSource创建的连接,该Map保存到线程自己的本地变量中,以便下次获取,下次线程获取连接时,首先去自己的本地变量中寻找map,看map释放为空,如果为空,说明没有绑定的连接,直接创建,若有,则通过jdbcTemplate中的DataSource对象作为key去map中拿出绑定的连接使用。
protected voiddoBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject =(DataSourceTransactionObject) transaction; Connection con = null; try{ if (txObject.getConnectionHolder() == null) {//如果当前的connectionholder不存在 Connection newCon = this.dataSource.getConnection();//则创建一个新连接 if(logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true);//并将该连接进行持有 } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con =txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel =DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); //Switch to manual commit if necessary. This is very expensive in some JDBC drivers, //so we don't want to do it unnecessarily (for example if we've explicitly //configured the connection pool to set it already). if(con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if(logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false);//为事务做准备 } txObject.getConnectionHolder().setTransactionActive(true); if (definition.getTimeout() !=TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(definition.getTimeout()); } //Bind the session holder to the thread. if(txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); }//重要的一步,将该连接和当前的线程进行绑定,并将该连接的自动提交设为false,为事务使用做准备 } catch (SQLException ex) {//出现异常则关闭任何打开的连接,物理释放 DataSourceUtils.releaseConnection(con, this.dataSource); throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
这时,status其实就是将连接和当前线程绑定,然后做一些必要的准备工作,如事务传播级别等等的设置:
public static void bindResource(Object key, Object value) throwsIllegalStateException { Assert.notNull(key, "Key must not be null"); Assert.notNull(value, "Value must not be null"); Map map = (Map) resources.get();//从ThreadLocal中获取当前连接信息的Map结构 //set ThreadLocal Map if none found if (map == null) { map = newHashMap(); resources.set(map);//如果未找到,则将其设置进去 } if(map.containsKey(key)) { throw new IllegalStateException("Already value [" + map.get(key) + "] for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]"); } map.put(key, value); if(logger.isDebugEnabled()) { logger.debug("Bound value [" + value + "] for key [" + key + "] to thread [" +Thread.currentThread().getName() + "]"); } }
如果想获得当前会话中执行的数据库连接,则使用以下方法:
Connection conn = DataSourceUtils.getConnection(dataSource);
这个获得和当前事务绑定的连接,否则使用getDataSource().getConnection()会创建一个全新的连接,这样会造成出现事务从而被隔离了。
当执行了getTransaction方法并返回一个TransactionStatus之后,表示事务已经开启了,后续所有的执行语句的操作都会使用一个数据库连接,而该连接在步骤1中会自动从当前线程的上下文中获取:
public staticObject getResource(Object key) { Assert.notNull(key, "Key must not be null"); Map map = (Map) resources.get();//从当前线程的上下文中拿到线程保持的map,为什么不直接将连接保持到resources中,而是放入一个map再放入resource中呢?原因是,类TransactionSynchronizationManager为一个事务同步的管理器,不光是为了持有连接,还有其他资源,所以,将线程自身其他的一些资源放入map,可以保寸多种对象不至于和其他线程产生冲突 if (map == null) { return null; } Object value = map.get(key);//如果是想拿到连接,此时的key是一个DataSource对象,说明是想从线程的变量集合中拿到本线程绑定的数据库连接 if (value != null &&logger.isDebugEnabled()) { logger.debug("Retrieved value [" + value + "] for key [" + key + "] bound to thread [" +Thread.currentThread().getName() + "]"); } returnvalue; }
未来后续的所有的操作使用的连接都将是该连接,而不是建立新的连接,这和不使用事务时有根本的区别。而事务开始之后,每个语句执行完毕之后,finally语句都会关闭连接释放资源,那么此时将会如何执行呢?
public static void doReleaseConnection(Connection con, DataSource dataSource) throwsSQLException { if (con == null) { return; } if (dataSource != null) { ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && conHolder.hasConnection() && connectionEquals(conHolder.getConnection(), con)) {//在status创建时初始化的holder对象在这个地方区分出到底是事务连接还是普通的非事务连接,如果是事务连接,那么执行Holder自己的释放方法 //It's the transactional Connection: Don't close it. conHolder.released(); return; } } //Leave the Connection open only if the DataSource is our //special data source, and it wants the Connection left open. if (!(dataSource instanceof SmartDataSource) ||((SmartDataSource) dataSource).shouldClose(con)) { logger.debug("Returning JDBC Connection to DataSource"); con.close();//这是不使用事务时使用的释放方法,即为物理关闭连接,执行完后,该连接即实实在在的关闭 } }
那么conHolder.released();如何执行呢?
public voidreleased() { this.referenceCount--; if (this.currentConnection != null) { this.connectionHandle.releaseConnection(this.currentConnection);//这是一个空方法 this.currentConnection = null;//释放引用 } }
protected voiddoCleanupAfterCompletion(Object transaction) { DataSourceTransactionObject txObject =(DataSourceTransactionObject) transaction; //Remove the connection holder from the thread, if exposed. if(txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.unbindResource(this.dataSource);//解除连接绑定 } //Reset connection. Connection con =txObject.getConnectionHolder().getConnection(); try{ if(txObject.isMustRestoreAutoCommit()) { con.setAutoCommit(true);//虽然将要关闭连接,但是仍然将该连接的自动提交恢复,为什么呢?原因就是,该连接可能是从连接池中拿到的,从连接池中拿到的连接并不会真正关闭,而是返回连接池,因此需要将该连接重置初始化,否则该连接被其他线程拿到时会影响执行结果,如无法自动提交,丢失事务等。可见spring想的非常周到。而且后续会将该连接一系列的属性重置,如事务的隔离级别、只读事务等等 } DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); //重置连接的一些属性,以为了连接重用 } catch(Throwable ex) { logger.debug("Could not reset JDBC Connection after transaction", ex); } if(txObject.isNewConnectionHolder()) { if(logger.isDebugEnabled()) { logger.debug("Releasing JDBC Connection [" + con + "] after transaction"); } DataSourceUtils.releaseConnection(con, this.dataSource);//最后会释放连接,由于连接已经和当前线程解除绑定,因此关闭连接是物理关闭(此处关闭的方法由数据库的驱动程序决定,驱动程序关闭时会判断是普通连接还是连接池连接,对应用程序是透明的) } txObject.getConnectionHolder().clear();//清除Holder信息,将当前为事务做的准备工作和信息全部清除归位 }