Spring集成Mybatis

SqlSessionFactoryBean
配置
<bean id="dataSources" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="driverClass" value="${jdbc.driver}"/>
	<property name="jdbcUrl" value="${jdbc.url}"/>
	<property name="user" value="${jdbc.user}"/>
	<property name="password" value="$jdbc.password"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSources"/>
	<property name="mapperLocations" value="classpath:mapper/*.mapper.xml"/>
</bean>
SqlSessionFactoryBean 介绍

每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 。 SqlSessionFactory 对 象 的 实 例 可 以 通 过 SqlSessionFactoryBuilder 对 象 来 获 得 。 SqlSessionFactoryBuilder 对象可以从 XML 配置文件,或从 Configuration 类的习惯准备的实 例中构建 SqlSessionFactory 对象

在Spring中,通常使用SqlSessionFactoryBean去创建一个共享的Mybatis(SqlSessionFactory)。
SqlSessionFactoryBean实现了两个接口:FactoryBean和InitializingBean。bean的创建过程中,少不了使用这两个接口,那么这两个接口的作用分别为:
FactoryBean:实现了此接口的类,通过getBean方法获取bean时其实是获取此类的getObject()返回的实例。
InitializingBean:实现此接口的bean会在初始化时调用其afterPropertieSet方法进行bean的逻辑初始化。

/**
 * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
 * This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context;
 * the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
 *
 * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction
 * demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions
 * which span multiple databases or when container managed transactions (CMT) are being used.
 *
 * @author Putthibong Boonbong
 * @author Hunter Presnall
 * @author Eduardo Macarron
 * @author Eddú Meléndez
 * @author Kazuki Shimizu
 *
 * @see #setConfigLocation
 * @see #setDataSource
 */
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
}

SqlSessionFactroyBean创建过程:在这里插入图片描述

  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();
  }

查看buildSqlSessionFactory方法的过程,其本质上与独立的Mybati创建过程无异,都是通过SqlSessionFactoryBuilder与Configuration创建一个DefaultSqlSessionFactory实例。在通过FactoryBean的getObject方法去获取对象的单例时,获取到的是一个DefaultSqlSessionFactory的单例。

 public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

后面会补充讲解Mybatis是如何创建DefaultSqlSessionFactory。

MapperFactoryBean 介绍
<bean id="signInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface"
				  value="com.dao.SignInfoMapper" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

同样实现了FactoryBean和InitializingBean在这里插入图片描述getObject方法

 public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

在独立的Mybatis我们也是通过SqlSession获取Mapper的代理对象,那么Spring是如何保证SqlSession在线程中的安全呢?getSqlSession方法获取到的又是什么样的对象呢?
通过分析MapperFactoryBean的类图,我们可以找知道getSession获取的其实是一个SqlSessionTemplate的对象,传递给Mapper代理工厂MapperProxy的也是此对象,那么在MapperProxy对Mapper进行方法代理时,会通过SqlSessionTemplate去执行相应的CRUD。看一下SqlSessionTemplate在执行insert时,是如何实现的。

public int insert(String statement) {
    return this.sqlSessionProxy.insert(statement);
  }

这个sqlSessionProxy是什么,什么时候进行初始化的?

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  	private final SqlSessionFactory sqlSessionFactory;

  	private final ExecutorType executorType;

 	private final SqlSession sqlSessionProxy;
 	
	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());
  }
}

在sqlSessionProxy中通过JDK动态代理去给sqlSessionProxy进行初始化,调用sqlSessionProxy时,会调用SqlSessionInterceptor的invoker方法。

 private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    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)) {
          // 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);
        }
      }
    }
  }

getSqlSession会为每一个线程分配一个SqlSession确保其在多线程环境下的安全性。

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

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

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }

    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值