Mybatis系列(十三)SQL执行流程分析(源码)

一、前言

前边的章节,我们介绍了mybatis配置的各个节点时如何实现的,以及mapper映射器中各个节点的含义日常使用的CRUD操作,基本都是单个节点或者属性的讲解,本着"打破砂锅学到底"的态度,本节将从mybatis的四大核心组件着手,从源码层面去剖析mybatis更深层的秘密。

  • SqlSessionFactoryBuilder(构造器):他会根据配置或者代码生成SqlSessionFactory,采用的是分步构建的Builder模式。
  • SqlSessionFactory(工厂接口):依靠它来生成SqlSession,使用的是工厂模式。
  • SqlSession(会话):一个既可以发送SQL执行返回结果,也可以获取Mapper的接口,在现有的技术中,一般我们会让其在业务逻辑代码中“消失”,而使用的是Mybatis提供的SQL
    Mapper接口编程技术,它能提高代码的可读性和可维护性。
  • SQL
    Mapper(映射器):Mybatis新设计存在的组件,它由一个java接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。

二、SqlSessionFactoryBuilder

SqlSessionFactoryBuilder作为mybatis的入口,通过建造者模式的设计思想,由builder建造者来创建SqlSessionFactory。

程序入口代码

String resource = "mybatis-config.xml";
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));

SqlSessionFactoryBuilder的build方法,掺入一个字节或者字符输入流,输入流就是我们传过来的配置文件mybatis-config.xml,不论你传的是Reader字符流还是InputStream字节流,最终都是执行一下build方法。

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

SqlSessionFactoryBuilder 对象调用XMLConfigBuilder 的parse()方法。

    public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

XMLConfigBuilder 会解析/configuration标签,configuration是Mybatis中重要的一个标签,(之前的章节对它的各个节点已经介绍过,感兴趣的可以查询mybatis系列学习的前边章节)

在 parseConfiguration 方法中,会对各个在 /configuration 中的标签进行解析

    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.settingsElement(root.evalNode("settings"));
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

关于各个节点的解析之前当前也有介绍,这里不再赘述,我们只要记住,最终解析的结果都会放到Configuration对象中。Configuration采用的是单例模式,几乎所有的Mybatis配置内容都会存放到这个单例对象中,以便后续将这些内容读出。

构建Configuration

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

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

它会做如下初始化:

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

三、SqlSessionFactory

有了Configuration对象,构建SqlSessionFactory是很简单的。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

通过以上的分析我们知道mybatis会根据文件流生成configuration对象,进而构建SqlSessionFactory对象,真正的难点在构建Configuration.
SqlSessionFactory 是 MyBatis 框架中的一个接口,它主要负责的是

  • 通过构建Configuration为mybatis做一系列初始化操作
  • 生成SqlSession

SqlSessionFactory 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory

  • DefaultSqlSessionFactory : SqlSessionFactory
    的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。

  • SqlSessionManager:基本已被废弃,内部自己维护了一个ThreadLocal,而mybatis更多的是和spring集成,这个线程池并没什么用

SqlSessionFactory 接口的方法

public interface SqlSessionFactory {
    SqlSession openSession();

    SqlSession openSession(boolean var1);

    SqlSession openSession(Connection var1);

    SqlSession openSession(TransactionIsolationLevel var1);

    SqlSession openSession(ExecutorType var1);

    SqlSession openSession(ExecutorType var1, boolean var2);

    SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);

    SqlSession openSession(ExecutorType var1, Connection var2);

    Configuration getConfiguration();
}

查询源码,我们可以看到SqlSessionFactory 提供了众多可重载的openSession方法用来获取SqlSession ,重点看最后一个方法getConfiguration,很显然是获取mybatis初始化时候存放到Configuration 的各种配置项。

上边讲到了SqlSessionFactory 的两个实现类,默认DefaultSqlSessionFactory ,随便点击一个openSession方法,最终实现类都指向下边这个方法。

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
        	//通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //通过excutor执行, excutor是对于Statement的封装
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

打开SqlSession接口,同样是有两个实现类,一个DefaultSqlSession,一个SqlSessionManager,默认为DefaultSqlSession实现类,SqlSessionManager依旧是维护了内部的一个线程池。

通过源码我们得知SqlSessionFactory 的openSession方法,最终返回的是SqlSession的默认实现类DefaultSqlSession,查询DefaultSqlSession源码,里边实现了众多CRUD的方法,还有 commit、 rollback、close 等方法,看到这里会不会觉得mybatis通过SqlSessionFactory 工厂类创建SqlSession,然后通SqlSession执行SQL,如果到此为止,那我们就是不是遗忘了什么东西,对,就是映射器Mapper,不能被眼前的虚虚实实遮蔽了双眼。仔细查看源码可以看到这么一段代码

Executor executor = this.configuration.newExecutor(tx, execType);

最终创建的DefaultSqlSession也是传入的executor ,其实这才是执行SQL的关键,我们先记的他在这里出现过。

拿到SqlSession后,接下来就是执行SQL的关键额,映射器
代码里边我们是这么写的

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

我们一层一层的去看

  1. SqlSession默认实现类DefaultSqlSession.getMapper
public class DefaultSqlSession implements SqlSession {
    public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }
 }
  1. 万能的Configuration对象
public class Configuration {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }
}
  1. 注册映射器 mapperRegistry
public class MapperRegistry {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }
}
  1. 映射器代理类 MapperProxy
public class MapperProxyFactory<T> {
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }
    
    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}
  1. 创建代理对象
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

这就是mybatis的核心流程,也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。通过动态代理我们就可以方便的在 Dao 层或者 Mapper 层定义接口,实现自定义的增删改查操作了

对jdk动态代理不太熟悉的可以看下这篇文章看完这篇,包你了解JDK动态代理

MapperProxyFactory会生成代理对象MapperProxy,最终调用mapperMethod.execute方法

    public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if (SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if (SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if (SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else {
            if (SqlCommandType.SELECT != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }

            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

execute方法包含了SQL的增删改查所有操作,我们以 selectList 为例,来看一下下面的执行过程。因为所有的查询最终都指向这个方法

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var6;
        try {
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
            var6 = result;
        } catch (Exception var10) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var10, var10);
        } finally {
            ErrorContext.instance().reset();
        }

        return var6;
    }

是不是发现了一个熟悉的东西executor,也就是说最终查询都是执行的这个方法executor.query。最先出现的地方是configuration创建了一个Executor 对象,我们来看看他到底是干什么用的:

四、Executor

位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutorCachingExecutor

  • BaseExecutor 是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配 的体现,是Executor的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是
    SimpleExecutor、ReuseExecutor 和 BatchExecutor。
  • SimpleExecutor : 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或select,就开启一个Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是PreparedStatment 对象)
  • ReuseExecutor : 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map把创建的Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor上的 Statement作用域是同一个 SqlSession。
  • BatchExecutor : 批处理执行器,用于将多个 SQL 一次性输出到数据库
  • CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。

Executor是怎么创建的呢,就是在我们获取SqlSession对象,调用openSession,通过configuration创建的。我们来看一下创建代码

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object 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 (this.cacheEnabled) {
            executor = new CachingExecutor((Executor)executor);
        }

        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

根据ExecutorType 枚举类型,分别初始化了BatchExecutorReuseExecutorSimpleExecutorCachingExecutor四种执行器,从源码可以看到如果未指定类型,则默认使用SimpleExecutor处理器。

ExecutorType 枚举类型

public enum ExecutorType {
    SIMPLE,
    REUSE,
    BATCH;

    private ExecutorType() {
    }
}

接着我们上边的selectList说executor.query方法,当第一次执行查询语句的时候,会调用他的实现类CachingExecutor,先查询缓存中是否存在,如果存在就从缓存中拿,或者不存在就执行SQL,并创建缓存。

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
            this.flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                this.ensureNoOutParams(ms, parameterObject, boundSql);
                List<E> list = (List)this.tcm.getObject(cache, key);
                if (list == null) {
                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    this.tcm.putObject(cache, key, list);
                }

                return list;
            }
        }

        return this.delegate.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 {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator i$ = this.deferredLoads.iterator();

                while(i$.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }

这里边源码很多,我们只看我们想看的queryFromDatabase

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }

        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

这就是SQL真正执行的地方,查看这个方法,会看到他的几个实现类,我们就看默认的SimpleExecutor简单执行器

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

获取configuration对象,通过configuration创建StatementHandler,然后再通过prepareStatement执行SQL,是不是想到了刚学习JDBC生活的预编译执行。下边我们再来看下StatementHandler。

五、StatementHandler

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 = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

很显然创建的真实对象是RoutingStatementHandler的对象,它实现了接口StatementHandler ,和Executor一样,用代理对象做一层层封装。
RoutingStatementHandler不是真实的服务对象,它通过适配器模式找到对应的
StatementHandler 来执行,在mybatis中,和Executor一样,RoutingStatementHandler分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler,它们分别对应的是JDBC中的Statement、PreparedStatment(预编译)和CallableStatement(存储过程处理),
在初始化RoutingStatementHandler对象时,会根据上下文环境决定创建哪个具体的
StatementHandler 对象。

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch(ms.getStatementType()) {
        case STATEMENT:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }

它定义了一个对象的适配器——delegate ,它是一个StatementHandler 接口对象,然后构造方法根据配置来适配对应的StatementHandler 对象,它的作用是给3个接口对象的使用提供一个统一且简易的适配器。
已最常用的 PreparedStatementHandler,看看mybatis是怎么执行查询的,Executor执行查询会执行StatementHandler 的prepare、parameterize和query方法。
prepare方法

    public Statement prepare(Connection connection) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var4) {
            this.closeStatement(statement);
            throw var4;
        } catch (Exception var5) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var5, var5);
        }
    }

instantiateStatement()方法是对SQL进行了预编译,然后做一些基础设置,比如超时、获取最大行数等设置。Executor中会调用它的parameterize()方法来设置参数。

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

最后调用query()方法

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        //PreparedStatement 的execute方法,是不是很熟悉
        ps.execute();
        //结果交给了ResultSetHandler 去处理
        return this.resultSetHandler.handleResultSets(ps);
    }

在执行前参数和SQL都被prepare()方法预编译,参数在parameterize()方法中已经进行了设置,所以主要执行SQL,然后返回结果就可以了,执行后我们看到了ResultHandler 对结果的封装和返回。
一条查询SQL的执行过程:Executor先调用StatementHandler的prepare()方法预编译SQL,同时设置一些基本的运行参数。然后用parameterize()方法启用ParameterHandler设置参数,完成预编译,执行查询,update()也是这样。如果是查询,mybatis会使用ResultHandler 封装结果返回。

至于ParameterHandler(参数处理器),以及ResultSetHandler(结果处理器),有空可以去看下源码,这里就不再讲解了。

SqlSession运行总结

在这里插入图片描述
SqlSession是通过执行器Executor调度StatementHandler来运行的,而StatementHandler经过3步:

  1. prepare预编译SQL
  2. parameterize设置参数
  3. query/update执行SQL

其中,parameterize是调用parameterHandler的方法设置的,而参数是根据类型处理器typeHandler处理的。query/update方法通过ResultSetHandler进行处理结果的封装,如果是update语句,就返回整数,否则就通过typeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回给调用者。这就是SqlSession的执行过程。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值