SpringトランザクションがMybatisのMybatisトランザクションにどのように統合されるか

@MapperScan登録

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
    
    
    
    //默认会生成MapperFactoryBean注册到容器中,下文重点分析
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

}

//MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    
    

  private ResourceLoader resourceLoader;

  /**
   * {@inheritDoc}
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        //...
    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
    
    
      if (StringUtils.hasText(pkg)) {
    
    
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
    
    
      if (StringUtils.hasText(pkg)) {
    
    
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
    
    
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    //扫描指定包路径下面的Mybatis接口类,生成BeanDefinition注册到Spring容器。
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }
}

ClassPathMapperScanner

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    
    
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    
    
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
    
    
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
	//...
      //可以看到注册的Bean为MapperFactoryBean
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      //...
      
    }
  }
  //省略其他代码        
}

MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
    

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    
    
  }
  
  public MapperFactoryBean(Class<T> mapperInterface) {
    
    
    this.mapperInterface = mapperInterface;
  }
  
    /**
   * 返回具体的对象,具体逻辑在父类中实现
   */
  @Override
  public T getObject() throws Exception {
    
    
    return getSqlSession().getMapper(this.mapperInterface);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<T> getObjectType() {
    
    
    return this.mapperInterface;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSingleton() {
    
    
    return true;
  }
  //省略其他代码...  
}

//父类SqlSessionDaoSupport中生成Mybatis接口对象的具体逻辑
public abstract class SqlSessionDaoSupport extends DaoSupport {
    
    

  private SqlSession sqlSession;

  private boolean externalSqlSession;

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

  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    
    
    this.sqlSession = sqlSessionTemplate;
    this.externalSqlSession = true;
  }


  public SqlSession getSqlSession() {
    
    
    return this.sqlSession;
  }

}

SqlSessionTemplate

このクラスはスレッドセーフなSqlSessionであり、インターフェイスプロキシオブジェクトの生成ロジックを提供します。

public class SqlSessionTemplate implements SqlSession, DisposableBean {
    
    
    
	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;
    //通过动态代理生成了一个由Spring托管的SqlSession
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] {
    
     SqlSession.class },
        new SqlSessionInterceptor());
  }
    
    //代理业务类
    private class SqlSessionInterceptor implements InvocationHandler {
    
    
    	@Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
          //此处会构造一个DefaultSqlSession,内部使用SpringManagedTransaction作为Exector的事务对象
          //注意:不是每次都构造,会将SqlSession绑定到线程,以便同一个线程获取的是同一个SqlSession
          SqlSession sqlSession = getSqlSession(
              SqlSessionTemplate.this.sqlSessionFactory,
              SqlSessionTemplate.this.executorType,
              SqlSessionTemplate.this.exceptionTranslator);
          try {
    
    
            //通过代理执行具体的sql逻辑
            Object result = method.invoke(sqlSession, args);
            //判断SqlSession是否处于事务中,如果不处于事务中则做一次手动提交,否则交由Spring的事务管理
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
    
    
              // force commit even on non-dirty sessions because some databases require
              // a commit/rollback before calling close()
              sqlSession.commit(true);
            }
            return result;
          } catch (Throwable t) {
    
    
            //...
          } finally {
    
    
            if (sqlSession != null) {
    
    
              closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }
          }
        }
    }
    
    
}

SpringManagedTransactionのデータベース接続オブジェクトの取得ロジック

@Override
public Connection getConnection() throws SQLException {
    
    
    if (this.connection == null) {
    
    
        openConnection();
    }
    return this.connection;
}

/**
   * Gets a connection from Spring transaction manager and discovers if this
   * {@code Transaction} should manage connection or let it to Spring.
   * <p>
   * It also reads autocommit setting because when using Spring Transaction MyBatis
   * thinks that autocommit is always false and will always call commit/rollback
   * so we need to no-op that calls.
   */
private void openConnection() throws SQLException {
    
    
    //通过DataSourceUtils对象获取连接
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

    if (LOGGER.isDebugEnabled()) {
    
    
        LOGGER.debug(
            "JDBC Connection ["
            + this.connection
            + "] will"
            + (this.isConnectionTransactional ? " " : " not ")
            + "be managed by Spring");
    }
}

//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 {
    
    
    //Spring事务会开启一个Connection并与当前线程绑定
    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();
    }
    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;
  }

まとめ

Springトランザクションは、接続を開いて現在のスレッドにバインドし、ThreadLocalオブジェクトを介して同じスレッドにバインドされたデータベース接続を取得して、Mybatisによって実行されるコードが同じデータベース接続を使用するようにします。SpringトランザクションはTransactionSynchronizationManagerオブジェクトを介してMybatisに関連付けられるため、@ Transactionalによって宣言されたメソッドのすべてのMybatisデータベース操作で同じデータベース接続が使用され、トランザクションの正確性が保証されます。

おすすめ

転載: blog.csdn.net/a1774381324/article/details/120913036