Mybatis源码分析-SqlSessionTemplate

承接Mybatis源码解析-MapperRegistry代理mapper接口,本文将在前文基础上讲解持久层会话的生成

SqlSessionFactory生成

在spring中,SqlSessionFactory的生成是通过SqlSessionFactoryBean生成的,如下

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
	  ****
	  ****
	  return this.sqlSessionFactoryBuilder.build(configuration);
  }

创建的SqlSessionFactory对象类型为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

上述创建的SqlSessionFactory接口最终会注入至前文所提及的MapperFactoryBean对象中,这里就不罗列SqlSessionFactory的接口方法列表了。主要功能就是获取建立Sql会话

MapperFactoryBean父类SqlSessionDaoSupport

在其父类有个setSqlSessionFactory()方法

  private SqlSession sqlSession;

  private boolean externalSqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      //包装成SqlSessionTemplate对象
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

mybatis最终会将SqlSession工厂包装成SqlSessionTemplate适配器。

SqlSessionTemplate构造函数

基本都是由SqlSessionFactory作为入参

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
	//默认的ExecutorType为ExecutorType.SIMPLE
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  }
  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;
    //对数据库的操作也会包装成代理的形式,所有的CRUD操作则都由SqlSessionInterceptor对象来完成
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

我们迫在眉睫的需要了解sqlSession代理的处理流程

SqlSessionTemplate#SqlSessionInterceptor

其是一个内部私有类

  private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //通过SqlSessionUtils.getSqlSession()获得真实处理CRUD的持久层,默认为DefaultSqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        //执行CRUD操作
        Object result = method.invoke(sqlSession, args);
        //非事务处理的数据操作需要强制commit
        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) {
          //关闭sqlSession,再上一个保险
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

创建sqlSession的步骤是通过SqlSessionUtils#getSqlSession()来完成的,我们必须需要仔细分析

SqlSessionUtils#getSqlSession()-获取真实的持久层操作对象

其实本质是从SqlSessionFactory中创建SqlSession的,这里只是作下缓存SqlSession

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

    notNull(sessionFactory, "No SqlSessionFactory specified");
    notNull(executorType, "No ExecutorType specified");
	//尝试从ThreadLocal中获取SqlSessionHolder,类似缓存
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    if (holder != null && holder.isSynchronizedWithTransaction()) {
      //不允许再次获取的SqlSession修改executorType
      if (holder.getExecutorType() != executorType) {
        throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
      }
	  //对引用同一个SqlSession的计数
      holder.requested();

      return holder.getSqlSession();
    }
	//通过SqlSessionFactory创建SqlSession
    SqlSession session = sessionFactory.openSession(executorType);

	//判断是否是同步的
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();
	  //TransactionFactory默认的为SpringManagedTransactionFactory
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
		//创建SqlSessionHolder并缓存在ThreadLocal中
        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");
        }
      }
    } 

    return session;
  }

我们知道上述的SqlSessionFactory为DefaultSqlSessionFactory,下面看下其如何创建SqlSession

DefaultSqlSessionFactory#openSessionFromDataSource

源码奉上

  //默认情况下,参数值为ExecutorType.SIMPLE/null/false
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    //事务接口对象
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      //此处的TransactionFactory为SpringManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //创建事务对象SpringManagedTransaction
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //根据execType创建池,默认为SimpleExecutor并支持缓存
      final Executor executor = configuration.newExecutor(tx, execType);
      //返回DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } 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();
    }
  }
  • 默认的事务对象则是由SpringManagedTransactionFactory来创建的,内含dataSource数据源,且不自动提交

  • 默认的Executor线程池则为SimpleExecutor并支持缓存,另外还有BatchExecutor/ReuseExecutor线程池。根据ExecutorType判断

  • 默认创建的SqlSession对象为DefaultSqlSession,内含事务对象、池、Configuration对象

SqlSessionTemplate#CRUD操作

根据前文的分析,SqlSessionTemplate的CRUD操作是由MapperMethod#execute()方法调用的,其中传给sqlSession的参数为mappedStatementId和其中的方法参数集合

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

简单梳理下service层调用CRUD操作的实现步骤

service层调用dao数据层方法-->读取对应接口dao的MapperFactoryBean#getObject()方法

-->SqlSessionTemplate#getMapper()方法-->得到MapperProxy代理类并调用对应接口方法的MapperMethod

-->接口类+method方法名作为mappedStatementId读取MappedStatement对象以获取SqlCommand指令-->根据SqlCommand指令调用SqlSessionTemplate对应的CRUD操作,一般为select()/delete()/update()/insert()方法

-->sqlSessionProxy代理调用真实数据层处理类DefaultSqlSession对应的CRUD操作-->选用池来引用MappedStatement对象处理数据库sql语句

-->返回结果集供MapperMethod处理返回给service层

CREATE/UPDATE/DELETE

简单从CREATE操作看,先看SqlSessionTempate#insert()方法

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

进而观察DefaultSqlSession#insert()方法

  public int insert(String statement) {
    return insert(statement, null);
  }
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }

进行仔细的观察我们得知,除了select语句,其实调用的都是update方法

  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      //根据statement得到相应的MappedStatement对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      //最终调用Executor池来进行相应的操作
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

而其中的wrapCollection()方法则是将参数分门别类下

  //StrictMap就是普通的HashMap,只是在get方法上加了必须存在相应key的判断
  private Object wrapCollection(final Object object) {
    if (object instanceof List) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("list", object);
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<Object>();
      map.put("array", object);
      return map;
    }
    return object;
  }

SELECT

直接转进DefaultSqlSession#select()通用方法

  public void select(String statement, Object parameter, ResultHandler handler) {
    select(statement, parameter, RowBounds.DEFAULT, handler);
  }
  public void select(String statement, ResultHandler handler) {
    select(statement, null, RowBounds.DEFAULT, handler);
  }
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用线程池的query方法来获取,获取到的值存放到ResultHandler内部属性中
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

小结

  1. SqlSessionTemplate接受dao层的CRUD请求,通过代理调用DefaultSqlSession的CRUD操作

  2. DefaultSqlSession的内部操作都是通过org.apache.ibatis.executor.Executor接口处理然后返回结果的

posted @ 2017-08-01 18:05  南柯问天  阅读(2278)  评论(0编辑  收藏  举报