Mybatis如何融入到Spring大家族的?

1.概述

1.MyBatis本质上是作为一个第三方的数据库的工具组件去更简单的实现与数据库的交互,以及实现在数据访问的过程中的一个性能的优化;
  既然作为第三方的一个组件,那么久需要融入到Spring这个大家庭,毕竟,在Java领域还是Spring是规范的制定者;
2.那么我们如何实现Mybatis与Spring的整合呢?
  下面我们进行介绍!

2.MyBatis集成Spring的项目版本:Mybatis-Spring

2.1.版本

1.MyBatis为了更好地实现与Spring集成交互,提供了一个项目 mybatis-spring
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.5</version>
</dependency>

大家可以去下面搜索版本,进行项目集成的引用
https://mvnrepository.com/
在这里插入图片描述

2.3.GitHub 源码地址

https://github.com/mybatis/spring
在这里插入图片描述

3.先看一下我们平时如何实现Mybatis集成到Spring中的配置

3.1.我随便找了一个项目的DB项目的applicationContext.xml

3.1.1.配置样例

<bean id="thirdDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">      
    <property name="driverClassName" value="${thirdDataSource.driverClassName}" />
    <property name="url">
        <value>${thirdDataSource.url}</value>
    </property>
    <property name="username">
        <value>${thirdDataSource.username}</value>
    </property>
    <property name="password">
        <value>${thirdDataSource.password}</value>
    </property>
    <property name="initialSize" value="10" />
    <property name="maxActive" value="40" />
    <property name="maxIdle" value="20" />
    <property name="minIdle" value="10" />
    <property name="maxWait" value="1000" />
    <property name="validationQuery" value="SELECT 1" />
    <property name="testOnBorrow" value="false" />
    <property name="testWhileIdle" value="true" /> 
</bean> 

<bean id="thirdSqlMapClient" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="thirdDataSource" />
    <property name="mapperLocations">
        <list>
                <value>classpath*:/com/gaoxinfu/ebiz/third/db/sqlmap/*.xml</value>
                <value>classpath*:/com/gaoxinfu/ebiz/third/trade/file/dao/sqlmap/*.xml</value>
                <value>classpath*:/com/gaoxinfu/ebiz/third/trade/notify/dao/sqlmap/*.xml</value>
                <value>classpath*:/com/gaoxinfu/ebiz/third/trade/order/dao/sqlmap/*.xml</value>
        </list>
    </property>
    <property name="plugins">
        <array>
            <bean class="com.gaoxinfu.common.util.mybatis.plugin.PagePlugin">
                <property name="dialect">
                    <value>mysql</value>
                </property>
            </bean>
        </array>
    </property>
</bean>
    
 <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.gaoxinfu.ebiz.third.db.dao,com.gaoxinfu.ebiz.third.**.dao" />
    <property name="sqlSessionFactoryBeanName" value="thirdSqlMapClient"></property>
</bean>

3.1.2.配置流程概述

1.首先,我们在进行项目应用搭建的时候,肯定会配置一个数据库实例(也就是连接数据库的原始的IP地址,用户名,密码,
  以及数据库连接的线程池相关信息)的Bean,比如我们这里的Bean就是com.alibaba.druid.pool.DruidDataSource,
  BeanId为thirdDataSource;
2.第二,SqlSessionFactoryBean的Bean创建,这里复制了属性:dataSource,mapperLocations(我们Sql-xml)
3.第三个Bean:MapperScannerConfigurer的创建,赋值了属性:basePackage和sqlSessionFactoryBeanName
关于具体的源码解析,下面我们进行讲解

3.1.3.配置流程图

在这里插入图片描述

4.MyBatis-Spring源码分析

4.1.从SqlSessionFactoryBean入手

在这里插入图片描述

1.从上面的配置样例我们知道SqlSessionFactoryBean注入了我们数据库的信息配置Datasource,
  Datasource我们不做描述,下面我们对SqlSessionFactoryBean源码进行分析;
public class SqlSessionFactoryBean
    implements
  //主要是实现其getObject方法
    FactoryBean<SqlSessionFactory>,
  //主要是实现了afterPropertiesSet方法
    InitializingBean,
    ApplicationListener<ApplicationEvent> {

  private static final Logger LOGGER = LoggerFactory.getLogger(SqlSessionFactoryBean.class);

  private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
  private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();

  private Resource configLocation;

  private Configuration configuration;

  private Resource[] mapperLocations;

  private DataSource dataSource;

  private TransactionFactory transactionFactory;

  private Properties configurationProperties;

  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

  private SqlSessionFactory sqlSessionFactory;

  // EnvironmentAware requires spring 3.1
  private String environment = SqlSessionFactoryBean.class.getSimpleName();

  private boolean failFast;

  private Interceptor[] plugins;

  private TypeHandler<?>[] typeHandlers;

  private String typeHandlersPackage;

  private Class<?>[] typeAliases;

  private String typeAliasesPackage;

  private Class<?> typeAliasesSuperType;

  private LanguageDriver[] scriptingLanguageDrivers;

  private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;

  // issue #19. No default provider.
  private DatabaseIdProvider databaseIdProvider;

  private Class<? extends VFS> vfs;

  private Cache cache;

  private ObjectFactory objectFactory;

  private ObjectWrapperFactory objectWrapperFactory;
 
  **中间这些都是get set方法 **
/**
   * {@inheritDoc}
   * {@link InitializingBean}
   *
   * 这里主要是是加载mybatis的相关配置
   */
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  /**
   * Build a {@code SqlSessionFactory} instance.
   *
   * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
   * {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a {@link Configuration}
   * instance directly(without config file).
   *
   * @return SqlSessionFactory
   * @throws Exception
   *           if configuration is failed
   *
   * 配置样例
   *   <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   *         <property name="configLocation" value="classpath:mybatis-config.xml"></property>
   *         <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
   *         <property name="dataSource" ref="dataSource"/>
   *   </bean>
   */
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      //初始化xmlConfigBuilder
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) != null)
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

4.1.1.属性字段

在这里插入图片描述

1.这里的属性配置主要是基于,我们在xml中进行配置的时候,自动设置其值;
 如下,两个值设置案例

在这里插入图片描述

4.1.2.实现方法

public class SqlSessionFactoryBean implements
  	//主要是实现其getObject方法
    FactoryBean<SqlSessionFactory>,
  	//主要是实现了afterPropertiesSet方法
    InitializingBean,
    ApplicationListener<ApplicationEvent>
4.1.2.1.实现方法-afterPropertiesSet,重点是buildSqlSessionFactory的调用
  /**
   * {@inheritDoc}
   * {@link InitializingBean}
   *
   * 这里主要是是加载mybatis的相关配置
   */
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }
 /**
   * Build a {@code SqlSessionFactory} instance.
   *
   * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
   * {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a {@link Configuration}
   * instance directly(without config file).
   *
   * @return SqlSessionFactory
   * @throws Exception
   *           if configuration is failed
   *
   * 配置样例
   *   <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   *         <property name="configLocation" value="classpath:mybatis-config.xml"></property>
   *         <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
   *         <property name="dataSource" ref="dataSource"/>
   *   </bean>
   */
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      //初始化xmlConfigBuilder
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) != null)
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

在这里插入图片描述

4.1.2.2.实现方法-getObject

在这里插入图片描述

4.1.3.总结

1.说白一点,SqlSessionFactoryBean的作用,主要是解析我们的表SQL的xml文件,使得关联mybatis
  使得我们数据库连接信息赋值到相关的MapperStatement上面
2.SqlSessionFactoryBean 从字面上,可以看出来,SqlSessionFactoryBean是为了产生SqlSessionFactory对象的;  

4.2.SqlSessionTemplate

4.2.1.认识 SqlSession

1.SqlSession定义了一系列的增删改查的方法,主要是用于数据库的执行交互;
  我们默认一般都用DefaultSqlSession类去处理;
  这里我们要讲解的就是SqlSession的另外一个实现 SqlSessionTemplate

在这里插入图片描述

4.2.2.SqlSessionTemplate的实现

4.2.2.1.首先实现:SqlSession【毫无疑问】实现一系列的增删改查的具体实现

在这里插入图片描述

4.2.2.2.其次实现:DisposableBean.destroy
1.实现了一个销毁的方法

在这里插入图片描述

4.2.2.SqlSessionTemplate特点:数据库交互是线程安全的

4.2.2.1.源码

线程的安全性主要是体现在这个方法上

 /**
   * Constructs a Spring managed {@code SqlSession} with the given {@code SqlSessionFactory} and {@code ExecutorType}. A
   * custom {@code SQLExceptionTranslator} can be provided as an argument so any {@code PersistenceException} thrown by
   * MyBatis can be custom translated to a {@code RuntimeException} The {@code SQLExceptionTranslator} can also be null
   * and thus no exception translation will be done and MyBatis exceptions will be thrown
   *
   * @param sqlSessionFactory
   *          a factory of SqlSession
   * @param executorType
   *          an executor type on session
   * @param exceptionTranslator
   *          a translator of exception
   */
  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());
  }
4.2.2.2.如何理解其线程安全的特性
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
由这行代码我们知道,这里使用了代理类SqlSessionInterceptor去生成SqlSession;
接下来,我们看一下 SqlSessionInterceptor类的invoke方法,是如何去生成SqlSession类的?
4.2.2.3.SqlSessionInterceptor如何生成SqlSession?
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      /**
       * 这里注意下,上面import处 直接引入方法
       * @see SqlSessionUtils#getSqlSession(SqlSessionFactory, ExecutorType, PersistenceExceptionTranslator)
       *
       * //获取同一个线程的复用sqlSession,如果没有则新生成一个并存到线程私有存储中
       */
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        /**
         * 判断是否被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) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          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);
        }
      }
    }
  }

SqlSessionUtils.getSqlSession

 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    /*
    事务管理器TransactionSynchronizationManager,SqlSessionHolder 持有器
    TransactionSynchronizationManager 本质上是对SqlSession的存储管理
    如果已经有了SqlSession,方法sessionHolder(executorType, holder);中会去获取sqlSessionHolder.getSqlSession();
     */
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

	//见面下面源码
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

	/*
	*如果没有则重新创建一个session
	*/
    LOGGER.debug(() -> "Creating a new SqlSession");
    session = sessionFactory.openSession(executorType);
    //重新创建的session  归入TransactionSynchronizationManager管理
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  
 private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder sqlSessionHolder) {
    SqlSession session = null;
    if (sqlSessionHolder != null && sqlSessionHolder.isSynchronizedWithTransaction()) {
      if (sqlSessionHolder.getExecutorType() != executorType) {
        throw new TransientDataAccessResourceException(
            "Cannot change the ExecutorType when there is an existing transaction");
      }

      sqlSessionHolder.requested();

      LOGGER.debug(() -> "Fetched SqlSession [" + sqlSessionHolder.getSqlSession() + "] from current transaction");
      session = sqlSessionHolder.getSqlSession();
    }
    return session;
  }

4.2.3.SqlSessionTemplate的使用

1.Mybatis集成Spring之后,我们用SqlSessionTemplate(线程安全类)去替换了DefaultSqlSession,
  那么我们如何使用SqlSessionTemplate?
  我们可以在Dao层直接进行注入这个类SqlSessionTemplate

举例说明

4.2.3.1.使用方式一:注入sqlSessionTemplate
import org.mybatis.spring.SqlSessionTemplate;

import javax.annotation.Resource;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-07-27 14:13
 */
public class EmpDAO {

    @Resource
    SqlSessionTemplate sqlSessionTemplate;

    private void query(){
        sqlSessionTemplate.select(String var1,  null);
    }

}
4.2.3.2.使用方式二:编写父类,注入sqlSessionTemplate
import org.mybatis.spring.SqlSessionTemplate;

import javax.annotation.Resource;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-07-27 14:20
 */
public class BaseDAO {

    @Resource
    SqlSessionTemplate sqlSessionTemplate;

}
import org.mybatis.spring.SqlSessionTemplate;

import javax.annotation.Resource;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-07-27 14:13
 */
public class EmpDAO extends BaseDAO{

    private void query(){
        sqlSessionTemplate.select(String var1,  null);
    }

}

4.2.4.SqlSessionDaoSupport的使用

1.针对SqlSessionTemplate的使用,Mybatis-Spring也提供了一个SqlSessionDaoSupport类来支持我们的快速使用,
  我们只需要DAO直继承SqlSessionDaoSupport即可
  这个类比较简单,主要是基于这种硬编码的方式使用

4.2.4.总结

1.不管第一种方式,还是第二种方式,这样写,有一个问题,就是每一个DAO都要去注入这个SqlSessionTemplate,还是比较麻烦的
    能否实现,自动的注入,然后让业务层的直接去使用DAO层
2.我们之前对Mybatis源码做过了解,对接DAO层的接口与Xml的Sql文件最终是通过MapperStatement以及MapperProxy反向代理去
  查找接口与Sql的对应关系的,但是现在这样看的话,显然,上面这种情况是Statement ID的硬编码,MapperProxy根本没用上
3.我们可以通过什么方式,不创建任何的实现类,就可以把 Mapper 注入到别的地方 使用,并且可以拿到 SqlSessionTemplate 操作
  数据库呢?这个问题就是我们Mybatis集成到Spring中之后,解决的一个问题:自动扫描

4.3.Mybatis-Spring自动扫描

    <bean id="thirdSqlMapClient" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="thirdDataSource" />
        <property name="mapperLocations">
            <list>
                    <value>classpath*:/com/gaoxinfu/ebiz/third/db/sqlmap/*.xml</value>
                    <value>classpath*:/com/gaoxinfu/ebiz/third/trade/file/dao/sqlmap/*.xml</value>
                    <value>classpath*:/com/gaoxinfu/ebiz/third/trade/notify/dao/sqlmap/*.xml</value>
                    <value>classpath*:/com/gaoxinfu/ebiz/third/trade/order/dao/sqlmap/*.xml</value>
            </list>
        </property>
    </bean>
        
        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.gaoxinfu.ebiz.third.db.dao,com.gaoxinfu.ebiz.third.**.dao" />
        <property name="sqlSessionFactoryBeanName" value="thirdSqlMapClient"></property>
    </bean>

4.3.1.接口的自动扫描

4.3.2.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东山富哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值