• 原生Mybatis框架源码解析


    前言:

        mybatis是我们常用的一种操作数据库的框架。

        我们在使用的mybatis有多种方式:原生mybatis、与Spring结合使用的mybatis、与SprinBoot结合使用的mybatis。

        使用的方式越来越简单,需要我们配置的项也越来越少,但是原理都是通用的,底层都是mybatis框架,而mybatis框架的底层也就是我们熟悉的JDBC。

        万变不离其宗,下面请随着笔者来以对照JDBC的方式来剖析mybatis源码,看完其源码可以发现其实框架没有我们想象中的那么难,只是封装的好一点,考虑的情况多一点

    1.JDBC操作数据库的方式

    String url = "jdbc:mysql://localhost:3306/test";
            String username = "root";
            String password = "root";
            String sql = "update blog set name = ? where id=?";
            
            try {
                // 1.获取连接
                Connection connection = DriverManager.getConnection(url, username, password);
                
                // 2.创建 preparedStatement
                PreparedStatement prepareStatement = connection.prepareStatement(sql);
                
                // 3.初始化参数
                prepareStatement.setString(1, "lucy");
                prepareStatement.setInt(2, 1);
                
                // 4.执行update
                prepareStatement.executeUpdate();
                
            } catch (SQLException e) {
                e.printStackTrace();

    主要分四步:获取连接、创建preparedStatement、封装参数、执行

        下面我们也按照这四步来分析Mybatis

    2.使用原生mybatis方式来操作数据库

    关于mybatis的详细使用读者可参考 易佰教程 https://www.yiibai.com/mybatis/install_configure.html  

        笔者直接摘录其中使用mybatis的经典方式,代码如下(注意:以下代码来自 易佰教程):

    public class HelloWord {
        private static SqlSessionFactory sqlSessionFactory;
        private static Reader reader;
     
        static {
            try {
                // 1.读取mybatis配置文件,并生成SQLSessionFactory
                reader = Resources.getResourceAsReader("config/Configure.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public static SqlSessionFactory getSession() {
            return sqlSessionFactory;
        }
        /**
         * @param args
         */
        public static void main(String[] args) {
            // 2.获取session,主要的CRUD操作均在SqlSession中提供
            SqlSession session = sqlSessionFactory.openSession();
            try {
                // 3.执行查询操作
                // 该方法包括三个步骤:封装入参数、执行查询、封装结果为对象类型
                User user = (User) session.selectOne(
                        "com.yiibai.mybatis.models.UserMapper.GetUserByID", 1);
                if(user!=null){
                    String userInfo = "名字:"+user.getName()+", 所属部门:"+user.getDept()+", 主页:"+user.getWebsite();
                    System.out.println(userInfo);
                }
            } finally {
                session.close();
            }
        }
     
    }

    通过上面的使用可知,最关键的代码就是session.selectOne(),里面包括了入参的封装、查询的执行、结果封装。这句话基本对应了JDBC中全部的四步关键操作。下面我们先来看下SqlSession的获取,然后再对其方法进行分析。

    3.SqlSession的获取

    // 1.读取config目录下Configure.xml文件
    Reader reader = Resources.getResourceAsReader("config/Configure.xml");
    // 2.使用SqlSessionFactoryBuilder创建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    // 3.在SqlSessionFactory中创建SqlSession
    SqlSession session = sqlSessionFactory.openSession();

    1)Resources.getResourceAsReader("config/Configure.xml")读取文件

      public static Reader getResourceAsReader(String resource) throws IOException {
        Reader reader;
        if (charset == null) {
          // 默认为null,走该步骤
          reader = new InputStreamReader(getResourceAsStream(resource));
        } else {
          reader = new InputStreamReader(getResourceAsStream(resource), charset);
        }
        return reader;
      }
     
    //getResourceAsStream()
      public static InputStream getResourceAsStream(String resource) throws IOException {
        return getResourceAsStream(null, resource);
      }
     
    //getResourceAsStream()
      public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        // ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) throw new IOException("Could not find resource " + resource);
        return in;
      }
     
    //classLoaderWrapper.getResourceAsStream(resource, loader)
      InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        for (ClassLoader cl : classLoader) {
          if (null != cl) {
     
            // 关键就是这句话
            InputStream returnValue = cl.getResourceAsStream(resource);
     
            // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
            if (null == returnValue) returnValue = cl.getResourceAsStream("/" + resource);
     
            if (null != returnValue) return returnValue;
          }
        }
        return null;
      }

    总结1):主要就是通过ClassLoader.getResourceAsStream()来获取指定classpath路径下的Resource

     2)new SqlSessionFactoryBuilder().build(reader)获取SessionFactory

    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
      }
     
    //build()
      public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          // 主要就是这句话
          // 实现了两个功能parse.parse()解析了xml;build(configuration)创建了SqlSessionFactory
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }

    * 下面看下parse.parse()方法如何进行xml解析

    //XMLConfigBuilder.parse()  
    public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }
     
    // parseConfiguration()
    // 可以看到主要是对xml各节点的分析,我们重点关注下mapperElement()方法
      private void parseConfiguration(XNode root) {
        try {
          propertiesElement(root.evalNode("properties")); //issue #117 read properties first
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          settingsElement(root.evalNode("settings"));
          environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
          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);
        }
      }
     
    // mapperElement(root.evalNode("mappers"))
      private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
              String mapperPackage = child.getStringAttribute("name");
              configuration.addMappers(mapperPackage);
            } else {
              // 1.获取resource信息,也就是对应的mapper.xml对应路径
              String resource = child.getStringAttribute("resource");
              String url = child.getStringAttribute("url");
              String mapperClass = child.getStringAttribute("class");
              if (resource != null && url == null && mapperClass == null) {
                // 2.解析对应的mapper.xml文件,
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                // 3.将解析出来的Mapper对象添加到Configuration中
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url == null && mapperClass != null) {
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }

    可以看到,通过解析configuration.xml文件,获取其中的Environment、Setting,重要的是将<mappers>下的所有<mapper>解析出来之后添加到Configuration,Configuration类似于配置中心,所有的配置信息都在这里

        * 解析完成之后,我们来看下parse(configuration)如何生成一个SQLSessionFactory

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

    非常简单,直接把Configuration当做参数,直接new一个DefaultSqlSessionFactory

     3)sqlSessionFactory.openSession()开启一个SqlSession

    public SqlSession openSession() {
        //configuration.getDefaultExecutorType() = ExecutorType.SIMPLE;
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
     
    // openSessionFromDataSource()
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          // 1.transactionFactory默认为 ManagedTransactionFactory
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          // 2.创建一个Transaction,注意该Transaction是org.apache.ibatis.transaction.Transaction
          // Connection即从此处获取的
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            
          // 3.创建一个Executor,默认为SimpleExecutor,参考Executor代码可知,主要的CRUD操作就是再此处完成的
          final Executor executor = configuration.newExecutor(tx, execType);
          // 4.将Executor和Configuration做为参数封装到DefaultSqlSession,默认返回该SqlSession
          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();
        }
      }

    总结3):该方法中有许多新的类出现,下面我们集中看下这些类,简单了解下这些类的作用,以便我们更好的理解代码

        * TransactionFactory

        类似于我们的SessionFactory,主要是用来生产Transaction的,注意这个Transaction是org.apache.ibatis.transaction.Transaction

    public interface TransactionFactory {
     
      void setProperties(Properties props);
      Transaction newTransaction(Connection conn);
      Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
    }

      * org.apache.ibatis.transaction.Transaction

        可以看到getConnection()方法返回的就是我们梦寐以求的java.sql.Connection,后续的操作都是基于此的

        并且还有一些关于事务的操作commit、rollback等

    public interface Transaction {
      // java.sql.Connection
      Connection getConnection() throws SQLException;
      void commit() throws SQLException;
      void rollback() throws SQLException;
      void close() throws SQLException;
    }

      * Executor

        根据其接口方法可以看到CRUD操作在这里被实现,看来SqlSession将具体的操作都委托为Executor了

    public interface Executor {
    ...
      int update(MappedStatement ms, Object parameter) throws SQLException;
     
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
     
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
     
      Transaction getTransaction();
        ...
     
    }

    总结3:

        * 解析configuration.xml文件,生成对应的Environment、Setting、Mapper,并注册到Configuration。Configuration相当于配置管理中心,所有的配置都在这里体现

        * 生成org.apache.ibatis.transaction.Transaction实现类,里面我们需要的java.sql.Connection

        * 根据Transaction封装Executor,Executor负责主要的CRUD操作

        * 将Configuration和Executor封装到SqlSession中,SqlSession对外提供统一操作入口,内部委托为Executor来执行

    4.SqlSession.selectOne()方法的执行过程

    SqlSession默认实现为DefaultSqlSession

    public <T> T selectOne(String statement, Object parameter) {
        // 直接调用了selectList()方法
        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;
        }
      }
     
    //selectList()
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          // 1.获取对应的MappedStatement
          // MappedStatement包装了每一个CRUD操作对应的详细信息
          MappedStatement ms = configuration.getMappedStatement(statement);
            
          // 2.executor默认实现为CachingExecutor
          List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
          return result;
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

    注意:有关于Cache的笔者就不分析了,网络上有很多关于Cache的文章,读者可自行查看

        可以看到主要的实现都委托给executor了,下面我们重点来看其query()方法的实现

    5.CachingExecutor.query()

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
     
    //query()
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        ...
        // 重点就是这句,默认实现在BaseExecutor
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
     
    //BaseExecutor.query()
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ...
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            // 重要实现在这里
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        ...
        return list;
      }
     
    //queryFromDatabase()
      private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          // 主要在这里,该方法是一个抽象方法,由子类实现,当前项目子类为SimpleExecutor
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          localCache.removeObject(key);
        }
        ...
      }
        
    //SimpleExecutor.doQuery()
          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();
          // 1.StatementHandler封装了JDBC Statement操作,如设置参数、将Statement结果集转换成List集合
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          // 2.封装preparedStatement的参数(主要功能就在这里实现)
          stmt = prepareStatement(handler, ms.getStatementLog());
          // 3.执行execute操作
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }

    总结:分析可知,主要的功能实现在StatementHandler中。

        * StatementHandler创建了PreparedStatement;

        * 封装了其所需要的参数;

        * 并且对其结果进行处理,封装为对象

        下面我们逐个方法来看

    1)configuration.newStatementHandler()获取StatementHandler

    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构造方法
      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());
        }
     
      }

    返回的StatementHandler为PreparedStatementHandler

    2)prepareStatement(handler, ms.getStatementLog())

    功能:获取Statement;封装PreparedStatement参数;

    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        // 1.获取java.sql.Connection
        // Connection connection = transaction.getConnection();
        Connection connection = getConnection(statementLog);
        // 2.获取对应的Statement
        stmt = handler.prepare(connection);
        // 3.封装参数
        handler.parameterize(stmt);
        return stmt;
      }

       * handler.prepare()获取Statement

        在这里,最终还是通过java.sql.Connection.prepareStatement(sql)的方式来创建Statement

    public Statement prepare(Connection connection) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          // 重点在这里
          statement = instantiateStatement(connection);
          setStatementTimeout(statement);
          setFetchSize(statement);
          return statement;
       ...
      }
       
    //PreparedStatementHandler.instantiateStatement()
      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 {
          // 采用了默认实现,
          // 直接对应了我们传统JDBC方式的从connection中获取PreparedStatement
          return connection.prepareStatement(sql);
        }
      }

     * handler.parameterize(stmt)封装参数

    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
     
    //
      public void setParameters(PreparedStatement ps) throws SQLException {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        // 1.parameterMappings包含了需要拼装的参数
        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;
              // 2.获取参数名称
              String propertyName = parameterMapping.getProperty();
              // 3.获取参数值
              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);
              }
              // 4.获取参数对应的类型处理器
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
              // 5.对应的封装参数操作还是要委托给TypeHandler来处理
              typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
          }
        }
      }
     
    //BaseTypeHandler.setParameter()
      public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
          if (jdbcType == null) {
            throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
          }
          try {
            ps.setNull(i, jdbcType.TYPE_CODE);
          } catch (SQLException e) {
            throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                    "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                    "Cause: " + e, e);
          }
        } else {
          // 由于参数值非空,会走该方法
          setNonNullParameter(ps, i, parameter, jdbcType);
        }
      }

    本例中参数类型为Integer,则对应的typeHandler为IntegerTypeHandler,我们可以看到

    //  IntegerTypeHandler.setNonNullParameter()
      public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
          throws SQLException {
        ps.setInt(i, parameter);
      }

    对应于JDBC的处理为preparedStatement.setInt(index,value)

        总结2):进过详细分析,我们可以看到,最终的处理还是JDBC那一套,通过connection创建preparedStatement;对preparedStatement进行参数封装;

        下面就是最终执行

    3)handler.<E>query(stmt, resultHandler)执行execute操作

    //PreparedStatementHandler.query() 
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 1.进行execute操作
        ps.execute();
        // 2.对结果进行封装
        return resultSetHandler.<E> handleResultSets(ps);
      }

    preparedStatement.execute()这个是常规操作,直接进行执行操作

        最后是对结果进行封装,交由resultSetHandler来操作

        写这篇博客太累了,哈哈,笔者就不打算继续分析resultSetHandler的操作了,但其核心操作也会与JDBC封装结果集一致的

    总结:分析下mybatis核心操作如下

        * 解析configuration.xml,生成Environment、Setting、Mapper等对象,并注册到Configuration

        * 从SqlSessionFactory中获取SqlSession,SqlSession作为操作入口,接收用户的数据库操作,并委托给内部的Executor来实现

        * Executor内部StatementHandler负责Statement的创建;PreparedStatement参数的注入;execute方法的执行,所以,可以说重要的执行操作都在StatementHandler中

        * execute方法执行过会,ResultSetHandler进行结果的封装

    推荐一篇很不错的博客 https://blog.csdn.net/luanlouis/article/details/40422941 

    以下内容来自该博客

    Mybatis的核心部件:

    SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
    Executor              MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
    StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
    ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数,
    ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
    TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
    MappedStatement   MappedStatement维护了一条<select|update|delete|insert>节点的封装, 
    SqlSource            负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
    BoundSql             表示动态生成的SQL语句以及相应的参数信息
    Configuration        MyBatis所有的配置信息都维持在Configuration对象之中。

    参考地址:https://blog.csdn.net/qq_26323323/article/details/81335058



  • 相关阅读:
    SSL证书安装指引
    腾讯云中ssL证书的配置安装
    微信小程序:微信登陆(ThinkPHP作后台)
    TPshop学习(8)微信支付
    LNMP安装Let’s Encrypt 免费SSL证书方法:自动安装与手动配置Nginx
    ruby文档
    tesseract-ocr图片识别开源工具
    Python读写文件
    百度贴吧的网络爬虫(v0.4)源码及解析
    中文分词库
  • 原文地址:https://www.cnblogs.com/loong-hon/p/12875900.html
Copyright © 2020-2023  润新知