spring事務源碼分析結合mybatis源碼(三)


下面將結合mybatis源碼來分析下,這種持久化框架是如何對connection使用,來達到spring事務的控制。

想要在把mybatis跟spring整合都需要這樣一個jar包:mybatis-spring-x.x.x.jar,這里面定義了一些主要的整合信息。

在spring配置文件中需要配置如下兩個bean:

    <!-- mybatis配置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource" />
        <property name="configLocation" value="classpath:mybatis.xml"></property>
        <!-- mybatis配置文件 -->
        <property name="mapperLocations" value="classpath:com/blackbread/dao/mapper/*.xml" />
    </bean>
    <!--mapper scanning -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.blackbread.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

首先讓我們來看org.mybatis.spring.SqlSessionFactoryBean,在這個類需要注入跟之前tx配置中一樣的dataSource。

SqlSessionFactoryBean類實現了InitializingBean接口,所以會執行afterPropertiesSet方法,在afterPropertiesSet方法中會執行buildSqlSessionFactory方法生成一個sqlSessionFactory對象,讓我們看下buildSqlSessionFactory方法:由於主要看的是跟spring tx結合的方式,所以代碼看不上很細,如有疏漏,望不吝賜教。

      protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
            Configuration configuration;
            XMLConfigBuilder xmlConfigBuilder = null;
            //初始化一個configuration
            if (this.configLocation != null) {
              xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
              configuration = xmlConfigBuilder.getConfiguration();
            } else {
              configuration = new Configuration();
              configuration.setVariables(this.configurationProperties);
            }
            if (this.objectFactory != null) {
              configuration.setObjectFactory(this.objectFactory);
            }
            if (this.objectWrapperFactory != null) {
              configuration.setObjectWrapperFactory(this.objectWrapperFactory);
            }
            if (hasLength(this.typeAliasesPackage)) {
              String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                  ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
              for (String packageToScan : typeAliasPackageArray) {
                configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                        typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
              }
            }
            //設置別名
            if (!isEmpty(this.typeAliases)) {
              for (Class<?> typeAlias : this.typeAliases) {
                configuration.getTypeAliasRegistry().registerAlias(typeAlias);
              }
            }
            //裝入插件,mybatis的插件都是以攔截器的形式進行的好像,比如分頁插件,這里是載入spring中注入的
            if (!isEmpty(this.plugins)) {
              for (Interceptor plugin : this.plugins) {
                configuration.addInterceptor(plugin);
              }
            }
            if (hasLength(this.typeHandlersPackage)) {
              String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
                  ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
              for (String packageToScan : typeHandlersPackageArray) {
                configuration.getTypeHandlerRegistry().register(packageToScan);
              }
            }

            if (!isEmpty(this.typeHandlers)) {
              for (TypeHandler<?> typeHandler : this.typeHandlers) {
                configuration.getTypeHandlerRegistry().register(typeHandler);
              }
            }
            //這里將解析mybatis.xml文件,載入所有配置,插件、setting等
            if (xmlConfigBuilder != null) {
              try {
                xmlConfigBuilder.parse();
              } catch (Exception ex) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
              } finally {
                ErrorContext.instance().reset();
              }
            }
            //這個很重要,這里定義了用的transactionFactory為SpringManagedTransactionFactory,這個在獲取
            //connection等地方都有用到,是mybatis跟spring的主要鏈接
            if (this.transactionFactory == null) {
              this.transactionFactory = new SpringManagedTransactionFactory();
            }
            //新建一個Environment對象,並將新建的transactionFactory放入其中
            Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
            configuration.setEnvironment(environment);
            if (this.databaseIdProvider != null) {
              try {
                configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
              } catch (SQLException e) {
                throw new NestedIOException("Failed getting a databaseId", e);
              }
            }
            if (!isEmpty(this.mapperLocations)) {
              for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                  continue;
                }
                try {
                //這里主要是解析配置的sql mapper配置文件
                  XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                      configuration, mapperLocation.toString(), configuration.getSqlFragments());
                  xmlMapperBuilder.parse();
                } catch (Exception e) {
                  throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                } finally {
                  ErrorContext.instance().reset();
                }
              }
            } else {
              }
            }
            return this.sqlSessionFactoryBuilder.build(configuration);
          }

看着好長的一段啊,其實這里做的工作就是解析配置文件生成configuration對象而已。在

 xmlMapperBuilder.parse();

這里將解析sql mapper文件中的映射關系生成MappedStatement對象,並執行 configuration.addMappedStatement(statement);放入到configuration對象中,有興趣的同學可以仔細看下。

這里主要需要注意的一塊就是this.transactionFactory = new SpringManagedTransactionFactory();這里就是mybatis跟spring連接到一起的地方。

接着我們看一下org.mybatis.spring.mapper.MapperScannerConfigurer對象的初始化過程,這個對象實現了BeanDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry方法中初始化一個對象ClassPathMapperScanner,並講執行scan--->doScan方法,

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
          for (BeanDefinitionHolder holder : beanDefinitions) {
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
            definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
            //實際就是將掃描到的接口包裝成MapperFactoryBean的實現類
            definition.setBeanClass(MapperFactoryBean.class);
            definition.getPropertyValues().add("addToConfig", this.addToConfig);
            boolean explicitFactoryUsed = false;
            //注入sqlSessionFactory對象,這個也很重要
            if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
              definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
              explicitFactoryUsed = true;
            } else if (this.sqlSessionFactory != null) {
              definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
              explicitFactoryUsed = true;
            }
            if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
              if (explicitFactoryUsed) {
                logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
              }
              definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
              explicitFactoryUsed = true;
            } else if (this.sqlSessionTemplate != null) {
              if (explicitFactoryUsed) {
                logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
              }
              definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
              explicitFactoryUsed = true;
            }

            if (!explicitFactoryUsed) {
              definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            }
          }
        return beanDefinitions;
      }

這段代碼其實主要就是從basePackage中掃描到相應的接口類,並且注冊到spring中,並且定義此對象的FactoryBean為:MapperFactoryBean,將返回如下對象

  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

最終其實就是生成Handler為MapperProxy的基於mapperInterface的代理類。

同時添加屬性:sqlSessionFactory。

這個操作很重要,在后面有關聯操作。

這里讓我們看下MapperFactoryBean類,這個類繼承自SqlSessionDaoSupport而在SqlSessionDaoSupport中有如下方法:

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

也就是上面調用的添加sqlSessionFactory屬性的set操作,在這個方法中初始話sqlSession,利用的是SqlSessionTemplate對象。

接下來讓我們看下SqlSessionTemplate的初始化過程:

 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

SqlSessionTemplate其實是實現了SqlSession接口的,在初始化的時候將生成一個sqlSessionProxy 代理類,可以查看下SqlSessionTemplate里面的所有與數據庫相關的操作都是通過sqlSessionProxy 這個代理類實現的。

接着看下sqlSessionProxy 的實際handler:

 private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

這里首先需要獲取一個SqlSession對象:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
            SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
            if (holder != null && holder.isSynchronizedWithTransaction()) {
              if (holder.getExecutorType() != executorType) {
                throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
              }
              holder.requested();
              return holder.getSqlSession();
            }
            SqlSession session = sessionFactory.openSession(executorType);
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
              Environment environment = sessionFactory.getConfiguration().getEnvironment();
              if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
                holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
                TransactionSynchronizationManager.bindResource(sessionFactory, holder);
                TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
                holder.setSynchronizedWithTransaction(true);
                holder.requested();
              } else {
                if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
                } else {
                  throw new TransientDataAccessResourceException(
                      "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
                }
              }
            } else {
            }
            return session;
          }

這里將會獲取一個SqlSessionHolder並判斷是否已經存在,如果不存在將會初始化一個新的,我們這里只分析第一次調用過程,也就是將會執行

 SqlSession session = sessionFactory.openSession(executorType);
--->openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
          return new DefaultSqlSession(configuration, executor);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

這里是主要的跟spring結合部分,讓我們仔細分析下,首先這里將獲取TransactionFactory: final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);這里將得到我們之前初始化時候加入的SpringManagedTransactionFactory。然后將初始化當前的tx:

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

然后將生成一個Executor ,將final Executor executor = configuration.newExecutor(tx, execType, autoCommit);這里在之前指定了execType為Simple,所以在這里將生成一個SimpleExecutor: executor = new SimpleExecutor(this, transaction);並將transaction加入屬性。

到這里SqlSession的初始化也就完成了,接下來就是通過反射進行實際方法的執行了:

  Object result = method.invoke(sqlSession, args);

以一個update操作來說明:

public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

這里首先將從configuration中根據操作的statement獲取映射內容MappedStatement ,

getMappedStatement(String id)---->getMappedStatement(String id, boolean validateIncompleteStatements)
接着將執行executor.update(ms, wrapCollection(parameter)),也就是實際的數據庫操作了,記得之前初始化的
executor么,這里就是對應的SimpleExecutor
 public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    clearLocalCache();
    return doUpdate(ms, parameter);
  }
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

這里主要是看prepareStatement方法:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }

然后看Connection 方法:

protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog);
    } else {
      return connection;
    }
  }

到這里看到了真正的Connection獲取方法:  transaction.getConnection();也就是通過之前注入的transaction中獲取connection,而這個transaction也就是對應的SpringManagedTransaction,他的調用過程getConnection()---->openConnection()

 private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
  }

這里其實就是調用了DataSourceUtils.getConnection(this.dataSource);來獲取一個Connection。

看看DataSourceUtils的getConnection(DataSource dataSource)--->doGetConnection(DataSource dataSource)

    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        //從TransactionSynchronizationManager中獲取ConnectionHolder,這個對象也就是之前我們第一次分析spring tx的時候
        //持有ConnectionHolder的對象了
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        //由於在前面的切面中已經開啟事務,並且初始化了ConnectionHolder所以這里將直接返回ConnectionHolder中的connection
        if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                conHolder.setConnection(dataSource.getConnection());
            }
            return conHolder.getConnection();
        }
        Connection con = dataSource.getConnection();
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            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,在spring tx中也是通過這個類中的resources (ThreadLocal對象)對ConnectionHolder進行持有的。

在這里將獲取到之前持有的ConnectionHolder對象,並從中獲取到Connection 對象然后返回,這樣就保證了spring tx中控制的Connection 跟實際調用的Connection為同一個Connection,也就可以通過spring tx對事務進行管理了。

后續的對數據的操作有興趣的可以自己讀一下,感覺mybatis的源碼沒有spring的那么清晰,還是需要仔細分析下才能整合到一起。

看的比較粗略,難免有疏漏地方,望不吝賜教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM