当前位置:   article > 正文

三、SqlSession的创建以及执行流程_sqlsession 对应的是一条sql么

sqlsession 对应的是一条sql么

简介

SqlSession接口提供了查询,插入,更新,删除方法,Mybatis中所有的数据库交互都由SqlSession来完成。SqlSession 对象完全包含以数据库为背景的所有执行 SQL 操作的方法,它的底层封装了 JDBC 连接,可以用 SqlSession 实例来直接执行已映射的 SQL 语句。每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不能被共享,也是线程不安全的,使用完成后需要及时关闭

SqlSession的创建

SqlSession 的创建需要借助于 SqlSessionFactory,SqlSessionFactory 是 Mybatis 的关键对象,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心。创建代码示例如下:

SqlSession sqlSession = sqlSessionFactory.openSession();
  • 1

SqlSessionFactory创建SqlSession的列表如下:

public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

直接分析默认使用的openSession()无参的方法,在DefaultSqlSessionFactory#openSession中,可以看到其调用的是openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)这个方法:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //1. 从配置中获取对应的环境
      final Environment environment = configuration.getEnvironment();
      // 获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //2. 根据数据源,事物隔离级别,是否自动提交创建事物管理器
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //3. 创建Executor执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //4. 返回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();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

该方法首先是根据事物工厂以及数据源工厂构造了一个事物管理器,之后创建一个Executor对象存入到DefaultSqlSession,后续DefaultSqlSession执行的时候便是通过Executor对象实例执行的。

Executor执行器的创建

Executor 接口定义了数据库操作的基本方法,其中 query*() 方法、update() 方法、flushStatement() 方法是执行 SQL 语句的基础方法,commit() 方法、rollback() 方法以及 getTransaction() 方法与事务的提交/回滚相关。Executor的是根据传入的executorType来创建的,有多个 Executor 接口的实现类,如下图所示:

在这里插入图片描述

创建Executor的代码如下:

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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

SqlSession的执行流程

创建Mapper

在获取到SqlSession后,根据SqlSession获取对应Mapper

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  • 1

SqlSession创建的Mapper是从Configuration中的mapperRegistry获取的:

// Configuration#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

根据MapperProxyFactory创建代理对象

MapperProxyFactory 的核心功能就是创建 Mapper 接口的代理对象,在 MapperRegistry 中会依赖 MapperProxyFactory 的 newInstance() 方法创建代理对象,底层则是通过 JDK 动态代理的方式生成代理对象的

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Mapper的执行

由上面的流程可知,通过SqlSession获取到的mapper对象是一个代理对象,其执行逻辑在InvocationHandler的invoke方法中

MapperProxy

通过分析 MapperProxyFactory 这个工厂类,我们可以清晰地看到MapperProxy 是生成 Mapper 接口代理对象的关键,它实现了 InvocationHandler 接口,接下来分析一下它的invoke方法

public class MapperProxy<T> implements InvocationHandler, Serializable {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是Object的方法,直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else {
                return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }

    private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
            // 尝试从methodCache缓存中查询方法对应的MapperMethodInvoker
            MapperMethodInvoker invoker = methodCache.get(method);
            if (invoker != null) {
                return invoker;
            }
            // 如果方法在缓存中没有对应的MapperMethodInvoker,则进行创建
            return methodCache.computeIfAbsent(method, m -> {
                if (m.isDefault()) {//针对默认方法
                    try {
                        // 这里根据JDK版本的不同,获取方法对应的MethodHandle的方式也有所不同
                        // 在JDK 8中使用的是lookupConstructor字段,而在JDK 9中使用的是
                        // privateLookupInMethod字段。获取到MethodHandle之后,会使用
                        // DefaultMethodInvoker进行封装
                        if (privateLookupInMethod == null) {
                            return new DefaultMethodInvoker(getMethodHandleJava8(method));
                        } else {
                            return new DefaultMethodInvoker(getMethodHandleJava9(method));
                        }
                    } catch (IllegalAccessException | InstantiationException | InvocationTargetException
                             | NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    // 对于其他方法,会创建MapperMethod并使用PlainMethodInvoker封装
                    return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException re) {
            Throwable cause = re.getCause();
            throw cause == null ? re : cause;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
MapperMethod

通过对 MapperProxy的invoke分析我们知道,MapperMethod 是最终执行 SQL 语句的地方,同时也记录了 Mapper 接口中的对应方法,其核心字段也围绕这两方面的内容展开

MapperMethod 的第一个核心字段是 command(SqlCommand 类型),其中维护了关联 SQL 语句的相关信息。在 MapperMethod$SqlCommand 这个内部类中,通过 name 字段记录了关联 SQL 语句的唯一标识,通过 type 字段(SqlCommandType 类型)维护了 SQL 语句的操作类型,这里 SQL 语句的操作类型分为 INSERT、UPDATE、DELETE、SELECT 和 FLUSH 五种。

SqlCommand
public static class SqlCommand {
    //记录了关联 SQL 语句的唯一标识
    private final String name;
    //维护了 SQL 语句的操作类型
    private final SqlCommandType type;

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
         获取Mapper接口中对应的方法名称
        final String methodName = method.getName();
        // 获取Mapper接口的类型
        final Class<?> declaringClass = method.getDeclaringClass();
        // 将Mapper接口名称和方法名称拼接起来作为SQL语句唯一标识,
        // 到Configuration这个全局配置对象中查找SQL语句
        // MappedStatement对象就是Mapper.xml配置文件中一条SQL语句解析之后得到的对象
        MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
                                                    configuration);
        if (ms == null) {
            // 针对@Flush注解的处理
            if (method.getAnnotation(Flush.class) != null) {
                name = null;
                type = SqlCommandType.FLUSH;
            } else {
                throw new BindingException("Invalid bound statement (not found): "
                                           + mapperInterface.getName() + "." + methodName);
            }
        } else {
            // 记录SQL语句唯一标识
            name = ms.getId();
            // 记录SQL语句的操作类型
            type = ms.getSqlCommandType();
            if (type == SqlCommandType.UNKNOWN) {
                throw new BindingException("Unknown execution method for: " + name);
            }
        }
    }

    public String getName() {
        return name;
    }

    public SqlCommandType getType() {
        return type;
    }

    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
                                                   Class<?> declaringClass, Configuration configuration) {
        //将Mapper接口名称和方法名称拼接起来作为SQL语句唯一标识
        String statementId = mapperInterface.getName() + "." + methodName;
        if (configuration.hasStatement(statementId)) {
            return configuration.getMappedStatement(statementId);
            // 如果方法就定义在当前接口中,则证明没有对应的SQL语句,返回null
        } else if (mapperInterface.equals(declaringClass)) {
            return null;
        }
        // 如果当前检查的Mapper接口(mapperInterface)中不是定义该方法的接口(declaringClass),
        // 则会从mapperInterface开始,沿着继承关系向上查找递归每个接口,
        // 查找该方法对应的MappedStatement对象
        for (Class<?> superInterface : mapperInterface.getInterfaces()) {
            if (declaringClass.isAssignableFrom(superInterface)) {
                MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                                                            declaringClass, configuration);
                if (ms != null) {
                    return ms;
                }
            }
        }
        return null;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
MethodSignature

MapperMethod 的第二个核心字段是 method 字段(MethodSignature 类型),其中维护了 Mapper 接口中方法的相关信息。

public static class MethodSignature {
    //表示方法返回值是否为 Collection 集合或数组、Map 集合、void、Cursor、Optional 类型
    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    private final boolean returnsOptional;
    //方法返回值的具体类型
    private final Class<?> returnType;
    //如果方法的返回值为 Map 集合,则通过 mapKey 字段记录了作为 key 的列名。mapKey 字段的值是通过解析方法上的 @MapKey 注解得到的。
    private final String mapKey;
    //记录了 Mapper 接口方法的参数列表中 ResultHandler 类型参数的位置。
    private final Integer resultHandlerIndex;
    //记录了 Mapper 接口方法的参数列表中 RowBounds 类型参数的位置。
    private final Integer rowBoundsIndex;
    //用来解析方法参数列表的工具类
    private final ParamNameResolver paramNameResolver;

    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
     // 根据返回值类型,初始化returnsVoid、returnsMany、returnsCursor、
     // returnsMap、returnsOptional这五个与方法返回值类型相关的字段
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
      this.returnsCursor = Cursor.class.equals(this.returnType);
      this.returnsOptional = Optional.class.equals(this.returnType);
      // 如果返回值为Map类型,则从方法的@MapKey注解中获取Map中为key的字段名称
      this.mapKey = getMapKey(method);
      this.returnsMap = this.mapKey != null;
      // 解析方法中RowBounds类型参数以及ResultHandler类型参数的下标索引位置,
      // 初始化rowBoundsIndex和resultHandlerIndex字段
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      // 创建ParamNameResolver工具对象,在创建ParamNameResolver对象的时候,
     // 会解析方法的参数列表信息
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
MapperMethod#execute

MapperMethod实例拥有了SqlCommand,MethodSignature对象之后,就可以开始执行Sql的逻辑了;根据SqlCommand不同的执行类型以及MethodSignature返回值类型调用SqlSession执行Sql语句获取到执行结果

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        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 if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
SqlSession#selectList

在MapperMethod中调用SqlSession的查询方法selectList,在SqlSession中最终是通过Executor执行sql逻辑,代码如下:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
     
      MappedStatement ms = configuration.getMappedStatement(statement);
      //executor执行
      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();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

总结

  1. SqlSession是通过SqlSessionFactory创建的,封装了Execcutor对象
  2. 获取Mapper接口是通过动态代理完成的,使用MapperProxyFactory创建Mapper代理对象,执行的时候通过MapperMethod中封装的SqlCommand获取绑定的sql,通过MethodSignature确定接口的返回值,最终统一调用Execcutor的逻辑完成整个数据库的操作
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/158231
推荐阅读
相关标签
  

闽ICP备14008679号