zoukankan      html  css  js  c++  java
  • Mybatis工作原理(九)

     mybatis工作流程:

    (1) SqlSessionFactoryBuilder 从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。

    (2) SqlSessionFactory生成SqlSession。

    (3) SqlSession拿到Mapper对象的代理(通过JDK动态代理生成一个Mapper的代理,代理类实现了我们写的Mapper接口)。

    (4) 通过MapperProxy调用Maper中相应的方法。

    1. 构建SqlSessionFactory过程

    构建主要分为2步:

    • 通过XMLConfigBuilder解析配置的XML文件,读出配置参数,包括基础配置XML文件和映射器XML文件;
    • 使用Configuration对象创建SqlSessionFactory,SqlSessionFactory是一个接口,提供了一个默认的实现类DefaultSqlSessionFactory。

    2. MappedStatement

    它保存映射器的一个节点(select|insert|delete|update),包括配置的SQL,SQL的id、缓存信息、resultMap、parameterType、resultType等重要配置内容。

    3. SqlSource

    它是MappedStatement的一个属性,主要作用是根据参数和其他规则组装SQL。

    4. BoundSql

    对于参数和SQL,主要反映在BoundSql类对象上,在插件中,通过它获取到当前运行的SQL和参数以及参数规则,作出适当的修改,满足特殊的要求。

    BoundSql提供3个主要的属性:parameterObject、parameterMappings和sql。

    parameterObject为参数本身,可以传递简单对象、POJO、Map或@Param注解的参数:

    • 传递简单对象(int、float、String等),会把参数转换为对应的类,比如int会转换为Integer;
    • 如果传递的是POJO或Map,paramterObject就是传入的POJO或Map不变;
    • 如果传递多个参数,没有@Param注解,parameterObject就是一个Map<String,Object>对象,类似这样的形式{"1":p1 , "2":p2 , "3":p3 ... "param1":p1 , "param2":p2 , "param3",p3 ...},所以在编写的时候可以使用#{param1}或#{1}去引用第一个参数;
    • 如果传递多个参数,有@Param注解,与没有注解的类似,只是将序号的key替换为@Param指定的name;

    parameterMappings,它是一个List,元素是ParameterMapping对象,这个对象会描绘sql中的参数引用,包括名称、表达式、javaType、jdbcType、typeHandler等信息。

    sql,是写在映射器里面的一条sql。

    SqlSession运行过程

    1. 映射器的动态代理

    Mapper映射是通过动态代理来实现的,使用JDK动态代理返回一个代理对象,供调用者访问。

    首先看看实现InvocationHandler接口的类,它是执行本代理方法的关键,可以看到,Mapper是一个接口,会生成MapperMethod对象,调用execute方法。

    public class MapperProxy<T> implements InvocationHandler, Serializable {
      
      .....
      
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }
    }

    MapperMethod采用命令模式,根据不同的sql操作,做不同的处理。

    public class MapperMethod {
      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;
            
            ......
            
          }
        }
      }

    最后看下,生成代理类的方法,就是使用JDK动态代理Proxy来创建的。

    public class MapperProxyFactory<T> {
    
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
    }

    总结下映射器的调用过程,返回的Mapper对象是代理对象,当调用它的某个方法时,其实是调用MapperProxy#invoke方法,而映射器的XML文件的命名空间对应的就是这个接口的全路径,根据全路径和方法名,便能够绑定起来,定位到sql,最后会使用SqlSession接口的方法使它能够执行查询。

    2. SqlSession下的四大对象

    映射器就是一个动态代理对象,进入到了MapperMethod的execute方法,它经过简单的判断就进入了SqlSession的删除、更新、插入、选择等方法。Mapper执行的过程是通过Executor、StatementHandler、ParameterHandler和ResultHandler来完成数据库操作和结果返回的。

    • Executor:执行器,由它统一调度其他三个对象来执行对应的SQL;
    • StatementHandler:使用数据库的Statement执行操作;
    • ParameterHandler:用于SQL对参数的处理;
    • ResultHandler:进行最后数据集的封装返回处理;

    在MyBatis中存在三种执行器:

    • SIMPLE:简易执行器,默认的执行器;
    • REUSE:执行重用预处理语句;
    • BATCH:执行重用语句和批量更新,针对批量专用的执行器;

    以SimpleExecutor为例,说明执行过程:

    public class SimpleExecutor extends BaseExecutor {
    
      /**
      * 执行查询操作
      */
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
      
      /**
      * 初始化StatementHandler
      */
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection);
        handler.parameterize(stmt);
        return stmt;
      }
      
      /**
      * 执行查询
      */
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = boundSql.getSql();
        statement.execute(sql);
        return resultSetHandler.<E>handleResultSets(statement);
      }
    }

    可以看到最后会委托给StatementHandler会话器进行处理,它是一个接口,实际创建的是RoutingStatementHandler对象,但它不是真实的服务对象,它是通过适配器模式找到对应的StatementHandler执行的。在MyBatis中,StatementHandler和Executor一样分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。

    Executor会先调用StatementHandler的prepare方法预编译SQL语句,同时设置一些基本运行的参数。然后调用parameterize()方法启用ParameterHandler设置参数,完成预编译,跟着执行查询,用ResultHandler封装结果返回给调用者。

    时刻与技术进步,每天一点滴,日久一大步!!! 本博客只为记录,用于学习,如有冒犯,请私信于我。
  • 相关阅读:
    c# Task多线程并行任务中等待所有线程都执行完成
    C#多线程之所有线程执行完成后
    正则表达式
    js 实时监听input中值变化
    js中prop和attr区别
    获取自定义属性
    checkBox
    js中判断数组中是否包含某元素的方法
    leetcode — path-sum-ii
    leetcode — path-sum
  • 原文地址:https://www.cnblogs.com/myitnews/p/11565178.html
Copyright © 2011-2022 走看看