zoukankan      html  css  js  c++  java
  • 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的那么清晰,还是需要仔细分析下才能整合到一起。

    看的比较粗略,难免有疏漏地方,望不吝赐教。

  • 相关阅读:
    [IOI2013]Dreaming
    Lost Cows
    Mobile Service
    [POI2005]Bank notes
    [CTSC2007]动物园zoo
    [CF1093F]Vasya and Array
    [雅礼集训 2017 Day1]市场
    [APIO2014]序列分割
    [CEOI2004]锯木厂选址
    [APIO2010]特别行动队
  • 原文地址:https://www.cnblogs.com/lcxdever/p/4579240.html
Copyright © 2011-2022 走看看