zoukankan      html  css  js  c++  java
  • Mybatis源码解析——执行流程

    Mybatis源码解析——执行流程

    ​ 吃了一年鱼,不知道是什么味道。这几天心血来潮手撸mybatis源码。

    ​ mybatis作为一个优秀的数据库框架,将sql语句等与java代码解耦,只要进行简单的配置,就可以对数据库进行操作。至于怎么配置,在我的其他篇博客有介绍,这里主要讲解执行流程。

    demo

    一、核心对象创建

    //就是一个简单的配置文件读取
    SqlSessionFactoryBuilder facbd = new SqlSessionFactoryBuilder();
    SqlSessionFactory fac=facbd.build(Resources.getResourceAsStream("xml"));
    SqlSession sqlsesssion = fac.openSession();
    
    //sqlsession源码简化,想详细看搜方法名
    //由下可以看出,就只是将配置文件通过XML解析后变为Java类,也就是Configuration核心对象。
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
     SqlSessionFactory var5;
    var5 = this.build(parser.parse());  //调用本类的build方法
     return va5
    }
    public SqlSessionFactory build(Configuration config) {
         return new DefaultSqlSessionFactory(config);
    }
    

    二、创建SqlSession对象,执行CRUD操作

    ​ sqlsession操作并不像标题那么简单,是相当的复杂!首先说明,不同的配置会产生的不同的核心对象,这里我所介绍的是开启二级缓存后的执行流程,因为涵盖的比较多,经此而已。

    ​ 在讲解之前先介绍核心对象相关的几个重要对象。

    1. TransactionFactory

      事务工厂对象,通过此对象可创建Transaction对象,Transaction这里使用的实现是JdbcTransaction,这个对象实际是只是注入了DataSource与当前使用的Connection对象,底层实际也是通过这个对象获取Connection连接对像,再通过此对象对事务进行提交!

    2. Excutor

      Excutor接口相比就麻烦多了,此接口有两个直接子类CachingExecutor和BaseExecutor,BaseExecutor是一个抽象类,他的实现类有着更加偏底层的代码。通过核心对象可以创建Excutor的实现类对象。开启二级缓存后创建的Excutor,返回的是CachingExcutor,内部注入BaseExcutor的实现类,对其进行了增强,也就是一些添加缓存记录的操作。没有配置缓存,就直接是BaseExecutor的实现类。这个对象是通过核心对象创建,注入到SQL Session的对象中。

    3. TransactionManager

      处理缓存的数据的对象,通过TransactionalCache将sql查询的结果,再次通过Cache的实现类进行操纵。自定义的二级缓存就是通过实现Cache接口,核心配置类配置,才会提交到redis或内部的HashMap中。通过核心对象传入参数到TransactionFactory工厂创建事务管理器,通过此对象对mybatis框架的事务进行提交。(Spring中已删除,使用Spirng事务管理平台)

    4. MappedStatement

      我们在使用mybatis时候,总是要写mapper文件,mapper文件每一个sql语句标签(指select、update等标签)都会生成MappedStatement对象。此对象也有一个Cache属性,用来存储缓存的结果。详细了解可以看我的mybatis二级缓存理解。这个对象主要就是获取sql语句了。

    sql语句主要可以分为四种,这里我只选其一,那就是select语句了.

    demo= sqlSession.selectOne("statementId");

    ​ selectOne方法也是一层层的方法调用,最终调用此类的selectList方法调用注入的Excutor实现类CachingExecutor,使用的是query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)方法,通过一些方法生成CacheKey,作为二级缓存(Map)的键,提高SqlSession多次相同的sql语句查询效率。

    以下是通过CachingExecutor最终调用的方法。

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

    从上的方法中,可以看出CachingExcutor的执行流程大致先查看缓存中有没有数据,有数据,就直接从缓存中拿,这里是通过上面TransactionManager对象获取缓存;如过没有数据,再通过代理的Excutor(实质执行者,为BaseExcutor的实现类)执行query。

    在BaseExcutor中实际调用的下面的方法。

    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 var8 = this.deferredLoads.iterator();
    
                    while(var8.hasNext()) {
                        BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                        deferredLoad.load();
                    }
    
                    this.deferredLoads.clear();
                    if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                        this.clearLocalCache();
                    }
                }
    
                return list;
            }
        }
    
    //  数据真实拉取调用的方法,此方法中有数据安全问题,具体会在下次博客中说。
    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;
        }
    

    由上面看出,也是与CachingExcutor雷同,内部也是封装了Excutor的子类。执行过程也是查看一级缓存中有没有缓存的结果,有就直接用;没有则从数据库拿。

    下面也就是最后的调用了。

    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;
        }
    
    // 通过TransactionManager获取连接对象,最后生成Statement返回。
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
            Connection connection = this.getConnection(statementLog);
            Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
            handler.parameterize(stmt);
            return stmt;
        }
    

    上面的方法相信学过jdbc的应该不陌生了,通过Statement执行sql语句逐级放回结果了。

  • 相关阅读:
    计算机图形学--贝塞尔曲线2
    计算机图形学--贝塞尔曲线1
    Windows编程1
    winform里面的Form1.Designer.cs
    mfc添加自定义事件
    怎么精确控制solidworks里面的表格的位置
    solidworks的工程图模板文件和图纸格式文件
    给datagridview的下拉框添加valueChange事件
    php开启短标签与<?xml version="1.0" encoding="UTF-8"?>冲突
    PHP分行打印数组-php输出数组方法大全
  • 原文地址:https://www.cnblogs.com/theStone/p/14452163.html
Copyright © 2011-2022 走看看