转自:http://blog.csdn.net/ch_space/article/details/18767875
闲来无事,重新看了下spring事务管理源码,写个笔记。
1、TransactionTemplate
当需要在事务中执行一个DB操作时,执行:
- transactionTemplate.execute(new TransactionCallback<MyDO>{
- public MyDO doInTransaction(TransactionStatus status){
- myDao.update();
- ...;
- myDao.insert();
- }
- });
事务中任何操作抛出异常,整个事务就会回滚掉,不用应用代码显式处理。
- public <T> T execute(TransactionCallback<T> action) throws TransactionException {
- if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
- return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
- }
- else {
- TransactionStatus status = this.transactionManager.getTransaction(this);
- T result;
- try {
- result = action.doInTransaction(status);
- }
- catch (RuntimeException ex) {
-
- rollbackOnException(status, ex);
- throw ex;
- }
- catch (Error err) {
-
- rollbackOnException(status, err);
- throw err;
- }
- catch (Exception ex) {
-
- rollbackOnException(status, ex);
- throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
- }
- this.transactionManager.commit(status);
- return result;
- }
- }
接下来是如何开启事务的?看transactionManager.getTransaction(this)
2、DataSourceTransactionManager
TransactionTemplate中包含一个dataSourceTransactionManager变量,用来管理事务,一切都是由他关联起来的。
- public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
- Object transaction = doGetTransaction();
- if (isExistingTransaction(transaction)) {
-
- return handleExistingTransaction(definition, transaction, debugEnabled);
- }
- ...
- try {
- boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
- DefaultTransactionStatus status = newTransactionStatus(
- definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
- doBegin(transaction, definition);
- prepareSynchronization(status, definition);
- return status;
- }
- }
3、来看下如何doBegin的:
- protected void doBegin(Object transaction, TransactionDefinition definition) {
- DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
- Connection con = null;
-
- try {
- if (txObject.getConnectionHolder() == null ||
- txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
- Connection newCon = this.dataSource.getConnection();
- txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
- }
-
-
- if (txObject.isNewConnectionHolder()) {
- TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
- }
- }
- ...
- }
现在事务开启了,链接也放在线程变量里面了,那如何跟dao操作关联起来呢?这里以ibatis为例分析下。
4、从SqlMapClient初始化看起:
- <bean id="mySqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
- <property name="dataSource" ref="myDataSource"/>
- <property name="configLocations">
- <list>
- <value>classpath:sqlmap/sqlmap-my.xml</value>
- </list>
- </property>
- </bean>
这个dataSource跟事务管理器的dataSource是同一个。看org.springframework.orm.ibatis.SqlMapClientFactoryBean初始化后:
- public void afterPropertiesSet() throws Exception {
- try {
- ...
-
- if (this.dataSource != null) {
- TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();
- DataSource dataSourceToUse = this.dataSource;
- if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {
- dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);
- }
- transactionConfig.setDataSource(dataSourceToUse);
- ...
- }
- }
- }
如果没猜错,spring在ibatis从这个代理数据源获取连接时候做了拦截处理,如果当前线程已经开启了事务,就直接用这个事务关联的连接了,看源码:
- public TransactionAwareDataSourceProxy(DataSource targetDataSource) {
- super(targetDataSource);
- }
5、继续看怎么取连接的:
- public Connection getConnection() throws SQLException {
- DataSource ds = getTargetDataSource();
- Assert.state(ds != null, "'targetDataSource' is required");
- return getTransactionAwareConnectionProxy(ds);
- }
- protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) {
- return (Connection) Proxy.newProxyInstance(
- ConnectionProxy.class.getClassLoader(),
- new Class[] {ConnectionProxy.class},
- new TransactionAwareInvocationHandler(targetDataSource));
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- ...
- Connection actualTarget = this.target;
- if (actualTarget == null) {
- actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);
- }
-
- if (method.getName().equals("getTargetConnection")) {
-
- return actualTarget;
- }
- ...
- }
6、看下DataSourceUtils的doGetConnection这个方法:
- public static Connection doGetConnection(DataSource dataSource) throws SQLException {
-
- 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();
- }
- ...
- }
好了,总结一下:
1)TransactionTemplate.execute执行事务块时,DataSourceTransactionManager开启事务,并把db连接用事务同步器TransactionSynchronizationManager注册到线程变量中中。
2)事务块中实行dao方法时,sqlMapClient从数据源获取连接是通过代理数据源取到了代理连接,在操作db的时候,这个代理连接返回了当前线程上下文中的连接。这样,同一个事务块中的多个dao操作就公用了同一个db连接,实现了事务悄无声息的封装。