zoukankan      html  css  js  c++  java
  • MyBatis的解析和运行原理(源码分析)

    本文将结合源码,分析mybatis基本的运行原理。导入Idea:参考

    MyBatis的解析和运行原理一览图

    MyBatis的运行过程分为两大步:

    • 读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory;
    • SqlSession的执行过程。

    MyBatis底层架构的基础掌握:

    • 反射技术
    • 动态代理技术

    1. 构建SqlSessionFactory的过程

    构建的两大步:

    • 第一步:通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析配置的XML文件,对出所配置的参数,并将读取的能容存入org.apache.ibatis.session.Configuration类对象中。而Configuration采用的是单例模式,几乎所有的MyBatis配置内容都会存放在这个单例对象中,以便后续将这些内容读出。
    • 第二步:使用Configuration对象去创建SqlSessionFactory。MyBatis中的SqlSessionFactory是个接口,而不是一个实现类,为此MyBatis提供了一个默认的实现类org.apache.ibatis.session.default.DefaultSqlSessionFactory。在大部分情况下都没有必要自己创建新的SqlSessionFactory实现类。

    这种创建的方式就是一种Builder模式,对于复杂的对象而言,使用构造参数很难实现。这时使用一个类Configuration作为统领,一步步构建所需要的内容,最终获取对象。

    1.1 构建Configuation

    首先来看看,xml是如何解析的。用XMLConfigBuider解析XML的源码。我们来看XMLConfiguration中的一段源码:

    package org.apache.ibatis.builder.xml;
    
    /**inmports  略 **/
    public class XMLConfigBuilder extends BaseBuilder {
        。。。。。。
            
         private void parseConfiguration(XNode root) {
            try {
                  //issue #117 read properties first
                  propertiesElement(root.evalNode("properties"));
                  Properties settings = settingsAsProperties(root.evalNode("settings"));
                  loadCustomVfs(settings);
                  loadCustomLogImpl(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
                  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);
            }
        }
    }
    

    从源码中我们可以看到,它是根据xml的标签节点名称一步步解析内容并得到对应的信息,而这些信息正是我们所配置的内容。那么这些读取的信息都去了哪里呢? 如typeHandlers的内容,读取后就注册到了TypeHandlerRegistry对象当中。XMLConfigBuilder类还继承了BaseBuilder类。我来看看BaseBuilder的部分源码:

    package org.apache.ibatis.builder;
    
    import ......
    
    public abstract class BaseBuilder {
      protected final Configuration configuration;
      protected final TypeAliasRegistry typeAliasRegistry;
      protected final TypeHandlerRegistry typeHandlerRegistry;
    
      public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
      }
    ......
    

    看到了,BaseBuilder定义了单例的configuration,而typeHandlerRegistry又是configuration的一个单例。不难想到,我们配置的typeHandlers内容被读取到先注册到了typeHandlerRegistry对象里面,然后最后又到了Configuration对象里。其他的配置也是相同的道理,最后能通过Configuration对象来获取。

    1.2 Configuration的作用

    在SqlSessionFactory构建中,Configuration是最重要的,它包含如下的作用:

    1. 读入配置文件,包括基础的配置文件XML和映射器的XML(或注解)。
    2. 初始化一些基础的配置,比如MyBatis的别名等;一些重要的类对象,如插件、映射器、Object工厂、typeHandlers对象等等。
    3. 提供单例,为后续创建SessionFactory服务,提供配置的参数。
    4. 执行一些重要的初始化对象方法。

    关于初始化,它会初始化如下的内容:

    • properties 全局参数
    • typeAliases 别名
    • Plugins 插件
    • objectFactory 对象工厂
    • reflectionFactory 对象包装工厂
    • settings 环境设置
    • environments 数据库环境
    • databaseIdProvider 数据库标识
    • typeHandlers 类型转换器
    • Mappers 映射器

    1.3 构建映射器(Mapper)的内部组成

    同样的,当XMLMapperBuilder解析XML时,会将每一个SQL和其配置的内容保存下来,那么他是怎么保存的呢?

    private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          sqlElement(context.evalNodes("/mapper/sql"));
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
      }
    

    读取完后最后到了这里 MappedStatement:

    package org.apache.ibatis.mapping;
    
    public final class MappedStatement {
    
      private String resource;
      private Configuration configuration;
      private String id;
      private Integer fetchSize;
      private Integer timeout;
      private StatementType statementType;
      private ResultSetType resultSetType;
      private SqlSource sqlSource;
      private Cache cache;
      private ParameterMap parameterMap;
      private List<ResultMap> resultMaps;
      private boolean flushCacheRequired;
      private boolean useCache;
      private boolean resultOrdered;
      private SqlCommandType sqlCommandType;
      private KeyGenerator keyGenerator;
      private String[] keyProperties;
      private String[] keyColumns;
      private boolean hasNestedResultMaps;
      private String databaseId;
      private Log statementLog;
      private LanguageDriver lang;
      private String[] resultSets;
    ......
    }
    

    着这里我么就能找到我们Mapper里面所配置的东西。其中private SqlSource sqlSource;是提供BoundSql的地方,它是一个接口,而不是一个实现类。表示从XML文件或注释中读取的映射语句的内容。它创建将从用户接收的输入参数传递到数据库的SQL。

    package org.apache.ibatis.mapping;
    
    /**
     * Represents the content of a mapped statement read from an XML file or an annotation.
     * It creates the SQL that will be passed to the database out of the input parameter received from the user.
     *
     * @author Clinton Begin
     */
    public interface SqlSource {
    
      BoundSql getBoundSql(Object parameterObject);
    
    }
    

    BoundSql是一个结果对象:

    package org.apache.ibatis.mapping;
    
    public class BoundSql {
    
      private final String sql;
      private final List<ParameterMapping> parameterMappings;
      private final Object parameterObject;
      private final Map<String, Object> additionalParameters;
      private final MetaObject metaParameters;
    
      public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
        this.sql = sql;
        this.parameterMappings = parameterMappings;
        this.parameterObject = parameterObject;
        this.additionalParameters = new HashMap<>();
        this.metaParameters = configuration.newMetaObject(additionalParameters);
      }
        ......
    }
    

    2. SqlSessiion运行过程

    2.1 映射器(Mapper)的动态代理

    先来看看MyBatis是如何实现getMapper方法的:

    public class DefaultSqlSession implements SqlSession {
        ......
        @Override
        public <T> T getMapper(Class<T> type) {
            return configuration.getMapper(type, this);
        }
        ......
    }
    
    

    我们看到它调用了Configuration对象的getMapper方法:

    public class Configuration {
        ......
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return mapperRegistry.getMapper(type, sqlSession);
        }
        ......
    }
    

    它又运用了Mapperregistry来获取对应的接口对象

    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);
        }
      }
    

    首先,它会判断是否注册了一个Mapper,如果没有则会抛出异常信息。如果有,就会启用MapperProxyFactory工厂来生成一个代理实例

    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.apache.ibatis.session.SqlSession;
    
    /**
     * @author Lasse Voss
     */
    public class MapperProxyFactory<T> {
    
      private final Class<T> mapperInterface;
      private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
    
      public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
      }
    
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    
    }
    
    

    Mapper映射是通过动态代理来的

    @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 (method.isDefault()) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }
    

    使用了JDK动态代理,先判断是不是类,使得话直接执行invoke方法。但是这里的Mapper是一个接口,所以生成了一个MapperMethod对象,通过cachedMapperMethod方法对其初始化,然后执行execute方法,把SqlSession和当前运行的参数传递过去。

     public Object execute(SqlSession sqlSession, Object[] args) {
         ......
    	result = executeForMap(sqlSession, args);
         ......
     }
    
    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.selectList(command.getName(), param, rowBounds);
        } else {
          result = sqlSession.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;
      }
    

    MapperMethod类采用命令模式运行,最终通过SqlSession去运行对象的SQL而已。

    总结:到了这里我们就能知道MyBatis为什么只用Mapper接口就可以运行了,因为Mapper的XML文件的命名空间对应的是这个接口的全限定名,而方法就是那条SQL的id,这样MyBatis就可以根据全路径和方法名,将其和代理对象绑定起来。通过动态代理技术,让这个接口运行起来,而后采用命令模式。最后使用SqlSession接口的方法使得它能够执行对应的SQL。

    2.2 SqlSession的四大对象

    • Executor代表执行器,由它调度StatementHandler、ParameterHandler、ResultSetHandler等来执行对应的SQL。其中StatementHandler是最重要的。
    • StatementHandler的作用是使用数据库的Statement(PtreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
    • ParameterHandler是用来处理SQL参数的。
    • ResultSetHandler是进行数据集(ResultSet)的封装返回处理的,它相当复杂,好在我们不经常用到它。

    Exector-执行器

    SqlSession其实只是一个门面,真正干活的是执行器。是一个真正执行Java和数据库交换的对象。有三种执行器(settings元素中的defaultExecutor可配置):

    • SIMPLE -简易执行器,没什么特别的,默认的执行器
    • REUSE-它是一种能够执行重用预处理语句的执行器。
    • BATCH-执行器重用语句和批量更新,批量专用的执行器。

    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;
      }
    

    判断配置类型确定创建哪种Executor,它的缓存则用CachingExecutor进行包装。最后interceptorChain.pluginAll(executor)运用插件的相关代码。

    我们用SimpleExecutor来看看Exector是如何调度的:

    public class SimpleExecutor extends BaseExecutor {
    
      public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @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);
        }
      }
    }
    

    configuration.newStatementHandle还是通过Configuration来创建StatementHandler的。然后使用prepareStatement对Sql编译和参数进行初始化。

     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;
      }
    

    prepareStatement实际进行调用了prepare方法进行预编译与基础设置,然后通过parameterize方法设置参数,最后使用StatementHandler的query方法,把resultHandler传递。

    StatementHandler-数据库会话器

    同样是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;
      }
    

    new RoutingStatementHandler很明显,创建的真实对象是RoutingStatementHandler。它实现了StatementHandler接口,用代理对象一层层的封装。

    然鹅RoutingStatementHandler却不是真实服务的对象。它是通过适配器模式来找到对应的StatementHandler来执行的。与Excutor一样,RoutingStatementHandler分为三种:

    • SimpleStatementHandler 对应JDBC的 Statement
    • PreparedStatementHandler 对应JDBC的 PreparedStatement
    • CallableStatementHandler 对应JDBC的 CallableStatement
    public class RoutingStatementHandler implements StatementHandler {
    
      private final StatementHandler delegate;
    
      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());
        }
    
      }
    }
    

    它定义了一个适配器-------delegate,适配器的作用就不说明了。接下来看看返回的StatementHandler是怎么执行查询的,以最常用的PreparedStatementHandler为例:

    package org.apache.ibatis.executor.statement;
    
    import ......;
    
    public class PreparedStatementHandler extends BaseStatementHandler {
    
      public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
      }
    
      @Override
      public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
      }
    
      @Override
      public void batch(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.addBatch();
      }
    
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleResultSets(ps);
      }
    
      @Override
      public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleCursorResultSets(ps);
      }
    
      @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() == ResultSetType.DEFAULT) {
          return connection.prepareStatement(sql);
        } else {
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
      }
    
      @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    
    }
    
    

    父类BaseStatementHandler:

    @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()方法是对SQL进行了预编译,然后做些基础配置,比如超时、获取的最大行数等的设置。Executor会调用parameterize()方法去设置参数。显然,这个时候设置参数的方法是由ParameterHandler来完成的。然后执行参数和SQL都被prepare()方法预编译了,参数在parameterize()方法中已经设置了,所以只要返回结果就可以了。执行后我们就能看到ResultSetHandler返回。

    到这里,MyBatis执行SQL的流程就比较清晰了。

    再来看看其他细节

    ParameterHandler--参数处理器

    使用它来对预编译语句进行参数设定的:

    package org.apache.ibatis.executor.parameter;
    
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public interface ParameterHandler {
      Object getParameterObject();//返回参数对象
      void setParameters(PreparedStatement ps) throws SQLException; //设置预编译SQL语句的参数
    }
    

    具体的实现类:

    package org.apache.ibatis.scripting.defaults;
    
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.apache.ibatis.executor.ErrorContext;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.ParameterMapping;
    import org.apache.ibatis.mapping.ParameterMode;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.type.JdbcType;
    import org.apache.ibatis.type.TypeException;
    import org.apache.ibatis.type.TypeHandler;
    import org.apache.ibatis.type.TypeHandlerRegistry;
    
    /**
     * @author Clinton Begin
     * @author Eduardo Macarron
     */
    public class DefaultParameterHandler implements ParameterHandler {
    
      private final TypeHandlerRegistry typeHandlerRegistry;
    
      private final MappedStatement mappedStatement;
      private final Object parameterObject;
      private final BoundSql boundSql;
      private final Configuration configuration;
    
      public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.configuration = mappedStatement.getConfiguration();
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
      }
    
      @Override
      public Object getParameterObject() {
        return parameterObject;
      }
    
      @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);
              }
            }
          }
        }
      }
    
    }
    

    分析可知,它还是从parameterObject对象中取到参数,然后使用typeHandler转换,如果没设置,根据签名注册的typeHandler对象参数进行处理。而typeHandler也是在MyBatis初始化时,注册在Configuration里面的,需要时就可以直接拿过来用。

    MyBatis就是这样完成参数设置的。

    ResultSetHandler----结果处理器

    package org.apache.ibatis.executor.resultset;
    
    import org.apache.ibatis.cursor.Cursor;
    
    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;
    
    }
    

    SqlSession运行总结

  • 相关阅读:
    python--tkinter桌面编程开发--记事本
    Python--面向对象编程
    Python--面向对象编程--时钟实例开发
    Python学习笔记--2--面向对象编程
    Python学习笔记--1
    epoll聊天室的实现
    操作系统--虚拟存储器
    操作系统--存储器管理
    操作系统--分页存储管理中逻辑地址转换为物理地址
    操作系统--处理机调度与死锁
  • 原文地址:https://www.cnblogs.com/nm666/p/11337169.html
Copyright © 2011-2022 走看看