首页 > 其他分享 >mybatis原理初探

mybatis原理初探

时间:2022-11-24 12:24:11浏览次数:53  
标签:executor return 初探 boundSql new mybatis 原理 null public

Mybatis执行原理初探

初始化

  1. 加载配置文件

    String resource = "mybatis.xml";
    
    // 加载mybatis的配置文件(它也加载关联的映射文件)
    InputStream inputStream = null;
    try {
        inputStream = Resources.getResourceAsStream(resource);
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  2. 构建sqlsession工厂

    // 构建sqlSession的工厂
    sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
  3. SqlSessionFactoryBuilder类中build方法

    // SqlSessionFactoryBuilder类
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse()); // 开始进行解析了 :)
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    
  4. XMLConfigBuilder类的parse方法解析配置文件和mapper文件,生成configuration

    // XMLConfigBuilder类
    public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            propertiesElement(root.evalNode("properties"));
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfs(settings);
            typeAliasesElement(root.evalNode("typeAliases"));
            pluginElement(root.evalNode("plugins"));
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
    
            /* 处理environments节点数据 */
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            typeHandlerElement(root.evalNode("typeHandlers"));
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
    
  5. SqlSessionFactoryBuilder类的build(configuration)方法得到XMLConfigBuilder的parse()方法的返回值configuration生成DefaultSqlSessionFactory,初始化完成

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

SqlSession

四个重要的对象

1.Executor执行器

调度执行StatmentHandler、ParameterHandler、ResultHandler执行相应的SQL语句

  • Executor执行器有三种:简易执行器SIMPLE(不配置就是默认执行器)、REUSE是一种重用预处理语句、BATCH批量更新、批量专用处理器

  • 执行器的作用:Executor会先调用StatementHandler的prepare()方法预编译SQL语句,同时设置一些基本的运行参数,然后调用StatementHandler的parameterize()方法(实际上是启用了ParameterHandler设置参数)设置参数,resultHandler再组装查询结果返回调用者完成一次查询完成预编译,简单总结起来就是即先预编译SQL语句,之后设置参数(跟JDBC的prepareStatement过程类似)最后如果有查询结果就会组装返回

  • Executor源码:

    • executor的来历:Configuration类的newExecutor()方法(下文举例返回SimpleExecutor对象)

      //Configuration类
      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;
        }
      
    • SimpleExecutor继承了BaseExecuror抽象类,BaseExecurot实现了Executor接口,simpleExecutor只有一个prepareStatement方法,其他方法均来自抽象类BaseExecutor

      public class SimpleExecutor extends BaseExecutor

      public abstract class BaseExecutor implements Executor

    • 调用prepareStatement()方法,在其中调用StatementHandler的prepare()和parameterize()方法

      //SimpleExecutor类 
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
          Statement stmt;
          Connection connection = getConnection(statementLog);
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);
          return stmt;
        }
      

2.StatamentHandler数据库会话器

使用数据库中Statement(PrepareStatement)执行操作,即底层是封装好了的prepareStatement

  • 作用:

    简单来说就是专门处理数据库会话。详细来说就是进行预编译并且调用ParameterHandler的setParameters()方法设置参数

  • StatementHandler有三种:

    SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分别对应Executor的三种执行器(SIMPLE、REUSE、BATCH)

  • StatementHandler源码

    • 来源:Configuration类中的newStatementHandler()方法,返回的实际是实现了StatementHandler接口的RoutingStatementHandler类

        //Configuration类
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
          StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
          statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
          return statementHandler;
      }
      

      RoutingStatementHandler根据传入的Executor执行器类型返回不同类型的StatementHandler(下文以SimpleStatementHandler为例)

      //RoutingStatementHandler类
      public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      
          switch (ms.getStatementType()) {
            case STATEMENT:
              delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            case PREPARED:
              delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            case CALLABLE:
              delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
              break;
            default:
              throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
          }
      
        }
      
    • SimpleStatementHandler类继承了抽象类BaseStatementHandler实现了StatementHandler接口

      public class SimpleStatementHandler extends BaseStatementHandler

      public abstract class BaseStatementHandler implements StatementHandler

    • 在BaseStatementHandler中重写prepare()方法,instantiateStatement()方法完成预编译,之后设置一些基础配置(获取最大行数,超时)

       @Override
        public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
          ErrorContext.instance().sql(boundSql.getSql());
          Statement statement = null;
          try {
            statement = instantiateStatement(connection);
            setStatementTimeout(statement, transactionTimeout);
            setFetchSize(statement);
            return statement;
          } catch (SQLException e) {
            closeStatement(statement);
            throw e;
          } catch (Exception e) {
            closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
          }
        }
      
    • *instantiateStatement*()预编译实际上也是使用了JDBC的prepareStatement()完成预编译

        @Override
        protected Statement instantiateStatement(Connection connection) throws SQLException {
          String sql = boundSql.getSql();
          if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = mappedStatement.getKeyColumns();
            if (keyColumnNames == null) {
              return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            } else {
              return connection.prepareStatement(sql, keyColumnNames);
            }
          } else if (mappedStatement.getResultSetType() != null) {
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
          } else {
            return connection.prepareStatement(sql);
          }
        }
      
    • parameterize()方法实际上调用了PreparedStatementHandler类的ParameterHandler的setParameters()方法

       @Override
        public void parameterize(Statement statement) throws SQLException {
          parameterHandler.setParameters((PreparedStatement) statement);
        }
      

3.ParameterHandler

处理Sql参数

  • 作用:对预编译中参数进行设置,如果有配置typeHandler,自然会对注册的typeHandler对参数进行处理

  • 源码

    • Mybatis提供了ParamterHandler的默认实现类DefalutParameterHandler

      public interface ParameterHandler {
      
        Object getParameterObject();
      
        void setParameters(PreparedStatement ps) throws SQLException;
      
      } 
      

      public class DefaultParameterHandler implements ParameterHandler

      ParameterHandler接口中的getParameterObject()方法是返回参数对象,setParameters()是设置预编译参数

        @Override
        public Object getParameterObject() {
          return parameterObject;
        }
      
    • 从parameterObject中取到参数,然后使用typeHandler(注册在Configuration中)进行参数处理:

        @Override
        public void setParameters(PreparedStatement ps) {
          ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
          List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
          if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
              ParameterMapping parameterMapping = parameterMappings.get(i);
              if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                  value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                  value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                  value = parameterObject;
                } else {
                  MetaObject metaObject = configuration.newMetaObject(parameterObject);
                  value = metaObject.getValue(propertyName);
                }
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                  jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                  typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                  throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
              }
            }
          }
        }
      

4.ResultSetHandler结果集处理器:

结果集ResultSet封装处理返回

  • 作用:组装结果返回结果集

  • 源码:

    • ResultSetHandler接口,handlerResultSets()是包装并返回结果集的,handleOutputParameters()是处理存储过程输出参数的

      public interface ResultSetHandler {
      
        <E> List<E> handleResultSets(Statement stmt) throws SQLException;
      
        <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
      
        void handleOutputParameters(CallableStatement cs) throws SQLException;
      
      }
      
    • Mybatis提供了默认的ResultSetHandler实现类DefaultResultSetHandler,其中重点是handlerResultSets()的实现

        @Override
        public List<Object> handleResultSets(Statement stmt) throws SQLException {
          ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
      
          final List<Object> multipleResults = new ArrayList<>();
      
          int resultSetCount = 0;
          ResultSetWrapper rsw = getFirstResultSet(stmt);
      
          List<ResultMap> resultMaps = mappedStatement.getResultMaps();
          int resultMapCount = resultMaps.size();
          validateResultMapsCount(rsw, resultMapCount);
          while (rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = resultMaps.get(resultSetCount);
            handleResultSet(rsw, resultMap, multipleResults, null);
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
      
          String[] resultSets = mappedStatement.getResultSets();
          if (resultSets != null) {
            while (rsw != null && resultSetCount < resultSets.length) {
              ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
              if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
              }
              rsw = getNextResultSet(stmt);
              cleanUpAfterHandlingResultSet();
              resultSetCount++;
            }
          }
      
          return collapseSingleResultList(multipleResults);
        }
      
    • 在Executor中doQuery()方法返回了封装的结果集

        @Override
        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(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.query(stmt, resultHandler);
          } finally {
            closeStatement(stmt);
          }
        }
      
    • 实际上是返回结果是调用了resultSetHandler的handleResultSets()方法

        @Override
        public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
          String sql = boundSql.getSql();
          statement.execute(sql);
          return resultSetHandler.handleResultSets(statement);
        }
      

标签:executor,return,初探,boundSql,new,mybatis,原理,null,public
From: https://www.cnblogs.com/yufou/p/16921438.html

相关文章

  • Spring @CrossOrigin注解原理是什么
    问题起源在Postman调用接口中,忘记设置Origin,发现@CrossOrigin未生效(响应头没有cors的)在filter中设置了Access-Control-Allow-Origin发现@CrossOrigin未生效(响应头没有co......
  • Java对象拷贝原理剖析及最佳实践
    作者:宁海翔1前言对象拷贝,是我们在开发过程中,绕不开的过程,既存在于Po、Dto、Do、Vo各个表现层数据的转换,也存在于系统交互如序列化、反序列化。Java对象拷贝分为深拷贝......
  • [第一篇]object c 和xcode开发工具撸代码初探吐槽
    开发工具吐槽开发工具似乎没有主动补全的快捷键.比如输入​​NSL​​手动把提示关闭了,没法再让它弹出勒(发现时按esc得,不知道能不能改...)开发工具似乎自动提示选中一个......
  • android动态换肤使用本地资源原理分析
    大致原理:在application里面注册所有activity回调这样可以实现很少的改动侵入性给LayoutFactory设置自己的​​factory2​​,工厂2使activity在​​setContentView​​调用......
  • SSH 原理及应用
    一、SSH简介SSH是SecureShell的简称,也称安全外壳协议。主要目的是实现安全远程登陆。二、SSH工作原理对数据的加密方式主要有两种:对称加密(密钥加密)(AES、DES)非......
  • IM通讯协议专题学习(三):由浅入深,从根上理解Protobuf的编解码原理
    本文由码农的荒岛求生陆小风分享,为了提升阅读体验,进行了较多修订和排版。1、引言搞即时通讯IM方面开发的程序员,在谈到通讯层实现时,必然会提到网络编程。那么计算机网络......
  • Hudi Upsert原理
    1.前言如果要深入了解ApacheHudi技术的应用或是性能调优,那么明白源码中的原理对我们会有很大的帮助。Upsert是ApacheHudi的核心功能之一,主要完成增量数据在HDFS/对象存......
  • SpringBoot3整合MyBatis报错:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate
    遇到了一个SpringBoot3整合MyBatis的问题,然后解决了。当然,这其实不是个大问题,只是自己编码时遇到了,然后总结总结分享一下。如果有遇到类似问题的,可以参考一下。交代......
  • 006. MyBatis基本使用---03MyBatis数据查询
    1.MyBatis数据查询步骤1.1创建实体类entity1.2创建mapper xml文件来说明当前sql语句是什么并编写<select>sql语句1.3在mybatis-config.xml开启驼峰映射1.4 在m......
  • QPSK调制解调原理(IQ调制)
    QPSK调制解调原理(IQ调制)作者:云外阳光链接:https://www.zhihu.com/question/23107539/answer/72521819 参考https://zhuanlan.zhihu.com/p/438504186参考https://zhua......