zoukankan      html  css  js  c++  java
  • 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接口处理然后返回结果的

  • 相关阅读:
    一道面试题:按照其描述要求用java语言实现快速排序
    Tomcat容器运行struts2+spring+mybatis架构的java web应用程序简单分析
    关于oracle存储过程的一些知识点
    多动手试试,其实List类型的变量在页面上取到的值可以直接赋值给一个js的Array数组变量
    Chapter 5: Container
    统计文件夹下java代码行数的小程序--主要是学习任务队列的思想
    利用strut2标签自动生成form前端验证代码
    简单实现web单点登录
    Chapter 4: Tomcat Default Connector
    [python] 格式化方法 format
  • 原文地址:https://www.cnblogs.com/question-sky/p/7269936.html
Copyright © 2011-2022 走看看