mybatis原理解析2

前文大致描述了Mybatis的构建阶段,下面我们来说说Mybatis的执行阶段:

1.Mybatis执行期间的关键类 (大致了解熟悉即可) :

SqlSession:Mybatis的核心执行入口,默认实现为DefaultSqlSession,提供了大量SQL调用方法

Configuration:保存构建阶段的结果,也负责执行阶段初始化所需要的变量

MappedStatement:配置好的映射SQL语句

BoundSql:存放SQL的执行内容

Executor:执行器,用于执行和数据库交互的组件,Mybatis有三种执行器,分别为:

                 (1) SIMPLE:简易执行器,不配置则为默认执行器

                 (2) REUSE:可以重用预处理语句

                 (3) BATCH:重用语句和批量更新

StatementHandler:处理SQL语句的管理器接口

ResultHandler:处理结果集

2.具体的执行流程:

public void test throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
		SqlSession openSession = sqlSessionFactory.openSession();

	}

这段代码大家应该十分熟悉,前文讲解了步骤1的执行流程,下面我们进入到流程2:

//调用openSession()会调用下面的构造函数创建实例
public SqlSession openSession() {return          
      this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }


//生成需要使用的SqlSession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
            //取出构建好的环境
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //生成执行器,很关键的一步,后文会在这继续深入下去
            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;
    }

在上面的源码中有初始化Executor实例这一步,下面我们来仔细看看这一步都做了哪些工作:

//根据配置生成对应的Executor实例
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;
    }

到了这里,SqlSession及其重要属性executor和configuration均已经构造完成,下面我们回到DefaultSqlSession这个类,看看这个类究竟是如何执行SQL语句查询的:

    private final Configuration configuration;
    private final Executor executor;
    private final boolean autoCommit;
    private boolean dirty;
    private List<Cursor<?>> cursorList;

    //DefaultSqlSession的构造函数
    public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
    }

//这是一个用于查询的方法,该类中还有其它类似的方法,本文以该方法为例子进行讲解
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            //下文是十分重要的两个步骤
            //1从构建好的configuration对象中获取MappedStatment对象
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            //2调用之前构建好的执行器执行query查询
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

        return var5;
    }

SqlSession的查询接口共有5大类查询方法,具体可以参照源码,这里以selectList()为例继续探究SQL执行流程:

    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对象,构建语句处理器
            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;
    }

    //预处理statementHandler,准备好执行环境
    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;
    }


    //在executor对象在执行具体的SQL时,通过Configuration类来创建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;
    }

在doQuery方法中,构建了语句处理器,并完成了对其的初始化工作,下一步则真正进入到查询工作:

public int update(Statement statement) throws SQLException {
        String sql = this.boundSql.getSql();
        Object parameterObject = this.boundSql.getParameterObject();
        KeyGenerator keyGenerator = this.mappedStatement.getKeyGenerator();
        int rows;
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
            statement.execute(sql, 1);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
        } else if (keyGenerator instanceof SelectKeyGenerator) {
            statement.execute(sql);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(this.executor, this.mappedStatement, statement, parameterObject);
        } else {
            statement.execute(sql);
            rows = statement.getUpdateCount();
        }

        return rows;
    }

//真正的执行工作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        //获取SQL语句的具体内容
        String sql = this.boundSql.getSql();
        //到这调用jdbc进行查询
        statement.execute(sql);
        //使用结果处理器堆查询结果进行加工处理
        return this.resultSetHandler.handleResultSets(statement);
    }

直到上述代码调用jdbc进行查询,在对结果进行加工处理,到这里,Mybatis的查询流程大体上就讲清楚了,下面来总结一下Mybatis的查询流程:
    1 根据配置文件(全局,sql映射)初始化出Configuration对象
    2 创建一个DefaultSqlSession对象,里面包含Configuration以及 Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
    3 DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
    4 MapperProxy里面有(DefaultSqlSession);
    5 执行增删改查方法:
           1) 调用DefaultSqlSession的增删改查(Executor);
           2) 创建StatementHandler,ParameterHandler和ResultSetHandler等加工处理对象
           3) 调用StatementHandler预编译参数以及设置参数值; 使用ParameterHandler来给sql设置参数
           4) 调用StatementHandler的增删改查方法;
           5) ResultSetHandler封装结果

至于时序图和类图我有时间的话会补上的,最近没有时间画,顺带推荐Visual Paradigm画图,真的顺手。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一个轻量级的Java持久层开源框架,它封装了JDBC操作数据库的底层细节,提供了一个简单易用的数据库访问方式。 Mybatis的源码分为核心模块和附加模块两部分,核心模块主要包括配置解析、SQL解析、SQL执行等功能,附加模块包括连接池、缓存、事务管理等功能。 在Mybatis的源码中,配置解析是其中的关键部分。通过解析mybatis-config.xml配置文件,可以获取到数据库连接信息、映射器配置、插件配置等。在配置解析过程中,Mybatis会对配置文件进行校验,确保配置的正确性。 SQL解析Mybatis的另一个重要功能。Mybatis通过解析Mapper接口中的注解或XML配置文件中的SQL语句,将SQL语句解析为ParameterMapping、BoundSql等对象,并将其封装成一个MappedStatement对象,供后续的SQL执行使用。 SQL执行是Mybatis的核心功能之一。在SQL执行阶段,Mybatis会根据MappedStatement中的信息,获取数据库连接,并执行对应的SQL语句。在执行过程中,Mybatis会通过TypeHandler对参数进行类型转换,并使用ResultSetHandler将查询结果封装成Java对象。 除了核心模块,Mybatis的源码还包括了连接池、缓存、事务管理等附加模块的实现。连接池模块负责管理数据库连接的获取和释放,缓存模块负责缓存查询结果以提高性能,而事务管理模块则负责管理数据库的事务处理。 总之,Mybatis的源码解析涉及多个关键模块的实现,包括配置解析、SQL解析、SQL执行、连接池、缓存、事务管理等。通过了解这些模块的实现原理,我们可以更好地理解和使用Mybatis框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值