• mybatis源码分析——SqlSession的作用


    sqlSession,顾名思义,是一次sql的回话,它起到了承上启下的作用,这个类既维护了Configuration对象,又包含Executor对象,可以

    执行jdbc操作,在mybatis体系中非常重要,下面我们来看一下这个类的源码:

    1:sqlSession类的源码

    SqlSession接口类定义了增删改查的操作:

    public interface SqlSession extends Closeable {
    
      /**
       * Retrieve a single row mapped from the statement key
       * @param <T> the returned object type
       * @param statement
       * @return Mapped object
       */
      <T> T selectOne(String statement);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter.
       * @param <T> the returned object type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return Mapped object
       */
      <T> T selectOne(String statement, Object parameter);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement, Object parameter);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter,
       * within the specified row bounds.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param rowBounds  Bounds to limit object retrieval
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * Eg. Return a of Map[Integer,Author] for selectMap("selectAuthors","id")
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param mapKey The property to use as key for each value in the list.
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, String mapKey);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param mapKey The property to use as key for each value in the list.
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param mapKey The property to use as key for each value in the list.
       * @param rowBounds  Bounds to limit object retrieval
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter
       * using a {@code ResultHandler}.
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param handler ResultHandler that will handle each retrieved row
       * @return Mapped object
       */
      void select(String statement, Object parameter, ResultHandler handler);
    
      /**
       * Retrieve a single row mapped from the statement
       * using a {@code ResultHandler}.
       * @param statement Unique identifier matching the statement to use.
       * @param handler ResultHandler that will handle each retrieved row
       * @return Mapped object
       */
      void select(String statement, ResultHandler handler);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter
       * using a {@code ResultHandler} and {@code RowBounds}
       * @param statement Unique identifier matching the statement to use.
       * @param rowBounds RowBound instance to limit the query results
       * @param handler ResultHandler that will handle each retrieved row
       * @return Mapped object
       */
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
    
      /**
       * Execute an insert statement.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the insert.
       */
      int insert(String statement);
    
      /**
       * Execute an insert statement with the given parameter object. Any generated
       * autoincrement values or selectKey entries will modify the given parameter
       * object properties. Only the number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the insert.
       */
      int insert(String statement, Object parameter);
    
      /**
       * Execute an update statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the update.
       */
      int update(String statement);
    
      /**
       * Execute an update statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the update.
       */
      int update(String statement, Object parameter);
    
      /**
       * Execute a delete statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the delete.
       */
      int delete(String statement);
    
      /**
       * Execute a delete statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the delete.
       */
      int delete(String statement, Object parameter);
    
      /**
       * Flushes batch statements and commits database connection.
       * Note that database connection will not be committed if no updates/deletes/inserts were called.
       * To force the commit call {@link SqlSession#commit(boolean)}
       */
      void commit();
    
      /**
       * Flushes batch statements and commits database connection.
       * @param force forces connection commit
       */
      void commit(boolean force);
    
      /**
       * Discards pending batch statements and rolls database connection back.
       * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
       * To force the rollback call {@link SqlSession#rollback(boolean)}
       */
      void rollback();
    
      /**
       * Discards pending batch statements and rolls database connection back.
       * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
       * @param force forces connection rollback
       */
      void rollback(boolean force);
    
      /**
       * Flushes batch statements.
       * @return BatchResult list of updated records
       * @since 3.0.6
       */
      List<BatchResult> flushStatements();
    
      /**
       * Closes the session
       */
      @Override
      void close();
    
      /**
       * Clears local session cache
       */
      void clearCache();
    
      /**
       * Retrieves current configuration
       * @return Configuration
       */
      Configuration getConfiguration();
    
      /**
       * Retrieves a mapper.
       * @param <T> the mapper type
       * @param type Mapper interface class
       * @return a mapper bound to this SqlSession
       */
      <T> T getMapper(Class<T> type);
    
      /**
       * Retrieves inner database connection
       * @return Connection
       */
      Connection getConnection();
    }
    

      

    实现类DefaultSqlSession维护了Configuration对象,可以直接从Configuration对象中拿到代理对象,也可以直接从Configuration对象中拿到MapperStatement对象

    然后就是对SqlSession接口类的具体实现

    public class DefaultSqlSession implements SqlSession {
    
      private Configuration configuration;
      private Executor executor;
    
      private boolean autoCommit;
      private boolean dirty;
    
      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
    
      public DefaultSqlSession(Configuration configuration, Executor executor) {
        this(configuration, executor, false);
      }
    
      @Override
      public <T> T selectOne(String statement) {
        return this.<T>selectOne(statement, null);
      }
    
      @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.<T>selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
    
      @Override
      public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
        return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
      }
    
      @Override
      public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
        return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
      }
    
      @Override
      public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        final List<? extends V> list = selectList(statement, parameter, rowBounds);
        final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
            configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
        final DefaultResultContext<V> context = new DefaultResultContext<V>();
        for (V o : list) {
          context.nextResultObject(o);
          mapResultHandler.handleResult(context);
        }
        return mapResultHandler.getMappedResults();
      }
    
      @Override
      public <E> List<E> selectList(String statement) {
        return this.selectList(statement, null);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public void select(String statement, Object parameter, ResultHandler handler) {
        select(statement, parameter, RowBounds.DEFAULT, handler);
      }
    
      @Override
      public void select(String statement, ResultHandler handler) {
        select(statement, null, RowBounds.DEFAULT, handler);
      }
    
      @Override
      public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public int insert(String statement) {
        return insert(statement, null);
      }
    
      @Override
      public int insert(String statement, Object parameter) {
        return update(statement, parameter);
      }
    
      @Override
      public int update(String statement) {
        return update(statement, null);
      }
    
      @Override
      public int update(String statement, Object parameter) {
        try {
          dirty = true;
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public int delete(String statement) {
        return update(statement, null);
      }
    
      @Override
      public int delete(String statement, Object parameter) {
        return update(statement, parameter);
      }
    
      @Override
      public void commit() {
        commit(false);
      }
    
      @Override
      public void commit(boolean force) {
        try {
          executor.commit(isCommitOrRollbackRequired(force));
          dirty = false;
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public void rollback() {
        rollback(false);
      }
    
      @Override
      public void rollback(boolean force) {
        try {
          executor.rollback(isCommitOrRollbackRequired(force));
          dirty = false;
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public List<BatchResult> flushStatements() {
        try {
          return executor.flushStatements();
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public void close() {
        try {
          executor.close(isCommitOrRollbackRequired(false));
          dirty = false;
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public Configuration getConfiguration() {
        return configuration;
      }
    
      @Override
      public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this);
      }
    
      @Override
      public Connection getConnection() {
        try {
          return executor.getTransaction().getConnection();
        } catch (SQLException e) {
          throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
        }
      }
    
      @Override
      public void clearCache() {
        executor.clearLocalCache();
      }
    
      private boolean isCommitOrRollbackRequired(boolean force) {
        return (!autoCommit && dirty) || force;
      }
    
      private Object wrapCollection(final Object object) {
        if (object instanceof Collection) {
          StrictMap<Object> map = new StrictMap<Object>();
          map.put("collection", object);
          if (object instanceof List) {
            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;
      }
    
      public static class StrictMap<V> extends HashMap<String, V> {
    
        private static final long serialVersionUID = -5741767162221585340L;
    
        @Override
        public V get(Object key) {
          if (!super.containsKey(key)) {
            throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
          }
          return super.get(key);
        }
    
      }
    
    }
    

      

    类中经常用到的方法:

    从configuration对象中获取代理对象

    从configuration对象中获取MappedStatement对象,执行jdbc操作

    2:sqlSession的创建以及使用

    如果要使用sqlSession,首先要从sqlSessionFactory中获取,sqlSessionFactory中维护了configuration对象

    SqlSession sqlSession = sqlSessionFactory.openSession();
    

      

      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
    

      

    从下面构造的方法中,可以看到构建defaultSqlSession的入参有3个,configuration对象,executor对象,是否自动提交标志

      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
          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();
        }
      }
    

      

    下面是newExecutor方法创建Executor的过程,因为executorType为null,所以使用的是SimpleExecutor

      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

      

     

    到这里,sqlSession对象完成初始化configuration和executor。

    如何使用sqlSession呢,上面已经提到类中比较重要的方法,下面我们再来串一遍

    a:调用sqlSessionFactory的openSession方法

     

     b:在openSessionFromDataSource中完成DefaultSqlSession对象的构建,主要是Executor的创建

    c:创建Executor

    从configuration中获取environment,这个对象封装了数据源,然后根据数据源创建Transaction对象,封装到Executor对象中

    最后创建DefaultSqlSession对象,返回。

    sqlSession的使用:

    a:获取代理对象

     

    从第一节解析命名空间,然后注册到mapperRegistry中,现在根据类型获取这个代理工厂对象,然后根据代理工厂创建代理

     

    调用代理工厂mapperProxyFactory的newInstance创建代理对象,然后返回,这里在数据绑定那一节提到过

     

    使用代理对象userMapper调用方法listUsers,实例会调用到切面MapperProxy的invoke方法

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
          try {
            return method.invoke(this, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }
    

      

    先抛开缓存,这里会调用MapperMethod的execute的方法 

    mapperMethod.execute(sqlSession, args);

      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 if (SqlCommandType.FLUSH == command.getType()) {
            result = sqlSession.flushStatements();
        } 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;
      }
    

      

    会调到select分支,多个返回值的方法

    executeForMany方法最终还是会调用到sqlSession的selectList方法

      private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        List<E> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
        } else {
          result = sqlSession.<E>selectList(command.getName(), param);
        }
        // issue #510 Collections & arrays support
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
          if (method.getReturnType().isArray()) {
            return convertToArray(result);
          } else {
            return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
          }
        }
        return result;
      }
    

      

     到这里我们知道具体的查询还是调用sqlSession的selectList,这里的逻辑就类似ibatis了,ibatis是直接把sqlId传入,更加sqlId找到MappedStatement,没有数据绑定的环节,

    下面的逻辑就是executor执行jdbc的逻辑,下一节我们再来分析

  • 相关阅读:
    Zookeeper 系列(五)Curator API
    Zookeeper 系列(四)ZKClient API
    Zookeeper 系列(三)Zookeeper API
    Zookeeper 系列(二)安装配制
    [bzoj 2393] Cirno的完美算数教室 (容斥原理+dfs剪枝)
    [Sdoi2013] [bzoj 3198] spring (hash+容斥原理)
    [bzoj 1471] 不相交路径 (容斥原理)
    [bzoj 3701] Olympic Games (莫比乌斯反演)
    [bzoj 2693] jzptab & [bzoj 2154] Crash的数字表格 (莫比乌斯反演)
    [51Nod 1244]
  • 原文地址:https://www.cnblogs.com/warrior4236/p/13091629.html
Copyright © 2020-2023  润新知