【Mybatis源码分析 4】Sqlsession进行CRUD操作的实现原理

目录

以一个select例子来看源码

1. 获取配置文件的字节流数据

2. 使用SqlSessionFactoryBuilder根据配置文件的字节流创建SqlSessionFactory对象

3. SqlSessionFactory的openSession方法提供SqlSession实例

4. SqlSession实例执行数据库操作

SqlSession四大重要的对象

1.  Executor

2. StatementHanlder

3. ParameterHandler

4. ResultSetHandler

简述sqlsession查询的运行过程,Executor执行器执行doQuery方法,假设SIMPLE模式

以一个select例子来看源码

package Service;

import Entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserService {


    @Test
    public void test() throws IOException {

        // 1. 获取配置文件的字节流数据
        InputStream inputStream= Resources.getResourceAsStream("mybatis-conf.xml");

        // 2. 使用SqlSessionFactoryBuilder根据配置文件的字节流创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);

        // 3. SqlSessionFactory的openSession方法提供SqlSession实例
        SqlSession sqlSession= sqlSessionFactory.openSession();

        // 4. SqlSession实例执行数据库操作
        List<User> list =sqlSession.selectList("selectAll");
        System.out.println(list);
    }

}

1. 获取配置文件的字节流数据

InputStream inputStream= Resources.getResourceAsStream("mybatis-conf.xml");

Resources是一个工具类,提供getResourceAsxxx(String)方法,从本地获取指定内容。调Resources.getResourceAsStream("mybatis-conf.xml")获取mybatis-conf.xml的字节流数据,提供相应的重载方法,指定加载mybatis-conf.xml的类加载器。

    InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        ClassLoader[] var3 = classLoader;
        int var4 = classLoader.length;

        // 遍历类加载器
        for(int var5 = 0; var5 < var4; ++var5) {
            ClassLoader cl = var3[var5];
            if (null != cl) {
                // 通过类加载器的getResourceAsStream方法读文本的字节流数据
                InputStream returnValue = cl.getResourceAsStream(resource);
                if (null == returnValue) {
                    returnValue = cl.getResourceAsStream("/" + resource);
                }

                // 如果成功获取字节流就返回
                if (null != returnValue) {
                    return returnValue;
                }
            }
        }

        return null;
    }

2. 使用SqlSessionFactoryBuilder根据配置文件的字节流创建SqlSessionFactory对象

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

org.apache.ibatis.session.SqlSessionFactoryBuilder是一个工厂类,提供build方法,通过Reader(字符流)或者InputStream(字节流),用于创建SqlSessionFactory。

    //通过mybatis配置文件字节流创建SqlSessionFactory对象(本质是创建SqlSessionFactory的实现类DefaultSqlSessionFactory的对象)    
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            // 通过XMLConfigBuilder解析mybatis配置文件
            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;
    }

    // 通过XMLConfigBuilder解析mybatis配置文件得到的配置信息创建SqlSessionFactory的实现类DefaultSqlSessionFactory的对象
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

3. SqlSessionFactory的openSession方法提供SqlSession实例

SqlSession sqlSession= sqlSessionFactory.openSession();

通过SqlSessionFactoryBuilder实例获取SqlSession实例,在这个期间主要是根据配置文件创建相应的执行器Executor实例。

    public SqlSession openSession() {
        // 无参的情况下,则使用SqlSessionFactory的configuration创建SqlSession对象
        // configuration.getDefaultExecutorType()方法表示获得执行器类型,默认是Simple,执行器有三种Simple,Reuse,Batch三种,如果在全局配置文件中不配置,默认就是Simple
        // 
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }


    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);
            // 使用事务和指定的执行器类型创建一个执行器(最重要,sqlsession就是通过这个executor去操作数据库的)
            Executor executor = this.configuration.newExecutor(tx, execType);
            // autoCommit是否开启事务自动提交,默认为false禁止事务自动提交。创建SqlSession的实现类DefaultSqlSession的实例
            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;
    }
    // 使用事务和指定的执行器类型创建一个执行器  
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        // 如果没有指定,默认Simple类型
        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 {
            // 我的例子中是默认的Simple类型的,所以我们后面就看一下这个SimpleExecutor类的实例exectuor它是怎么执行数据库操作的
            executor = new SimpleExecutor(this, transaction);
        }

        if (this.cacheEnabled) {
            executor = new CachingExecutor((Executor)executor);
        }

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

4. SqlSession实例执行数据库操作

List<User> list =sqlSession.selectList("selectAll");

   // SqlSession接口方法在DefaultSqlSession实现类中实现
   // String statement由mapper.xml或者@Select注解提供sql语句节点
   // Object parameter可变的参数个数,本质是一个集合,可以通过索引获取其中的参数,也可以通过实参名获取
   // RowBounds rowBounds逻辑分页,取offset-limit位置的数据记录,默认offset=0,limit为int的最大值
   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            // 通过configure对象和java传入参数statement获得一个映射mapper.xml中的statementid或者mapper接口注解的MappedStatement对象
            // 注意statement参数是个String对象,内容必须与mapper.xml中的statementid或者接口中注解方法名一致,才能正确映射到该条sql语句
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            // 执行器调用父类BaseExecutor的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;
    }

    // ResultHandler resultHandler 对查询得到的结果集ResultSet封装处理返回
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //ms.getBoundSql内部调用RawSqlSource的getBoundSql方法,该方法又调用了StaticSqlSource的getBoundSql方法,并在该方法内部初始化了一个BoundSql对象
        BoundSql boundSql = ms.getBoundSql(parameter);
        //createCacheKey是调用的BaseExecutor方法根据mappedStatement的id,rowBounds的offset、limit值、要执行的sql语句、传递的参数、environment的id来创建cacheKey。
在query的时候查看是否有cache,如果有则使用cache结果,否则使用内部delegate的query方法,这里跳转到了BaseExecutor的query方法,该方法内部又使用了queryFromDatabase方法。
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    //BaseExecutor的query方法
    // MappedStatement ms, 映射sql节点的statement
    // Object parameter, 查询条件的参数
    // RowBounds rowBounds, 逻辑分页
    // ResultHandler resultHandler, 
    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;
        }
    }


    // 顾名思义,从数据库中查询,本质是调用deQuery方法
    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 {
            // doQuery方法在BaseExecutor中是一个抽象方法,在执行器自己的实现类中实现(BatchExecutor\ReuseExectuor\SimpleExecutor, 我的例子中用的SimpleExecutor,所以直接看SimpleExecutor实现的doQuery吧)
            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;
    }
    // SimpleExecutor实现的BaseExecutor的doQuery方法
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            // 获取MappedStatement对象的配置信息
            Configuration configuration = ms.getConfiguration();
            // 创建一个StatementHandler对象,可以认为它是一个Statement执行者,负责JDBC和数据库之间statement的交互
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 采用的是预编译的方式,根据查询条件,得到sql语句的Statement对象
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            // Statement执行者执行query方法,执行查询语句,返回查询结果
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }
        // Executor会执行StatementHandler的prepare()方法进行预编译---->填入connection对象等参数---->再调用parameterize()方法设置参数---->完成预编译
    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;
    }
    // java.sql.PreparedStatement的实现类com.mysql.cj.jdbc.PreparedStatementWrapper类的query方法
    // 通过预编译statement执行查询,返回resultSetHandler封装的结果
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        return this.resultSetHandler.handleResultSets(ps);
    }


SqlSession四大重要的对象

mybatis本质就是封装了jdbc对数据库的操作,让程序员讲主要精力放在sql上,通过mybatis提供提供的映射方式,自由灵活生成(半个ORM框架,半自动化,sql需要程序员书写)满足业务需求,mybatis将preparedStatement中的输入参数自动进行输入映射,并且将查询结构灵活地映射为java类型。

1.  Executor

执行器起到至关重要的作用,它是真正执行Java与数据库交互的东西,参与了整个SQL查询执行过程中。

主要有三种执行器类型-简易执行器SIMPLE(不配置就是默认执行器)、REUSE是一种重用预处理语句、BATCH批量更新、批量专用处理器,分别对应SimpleExecutor、ReuseExecutor、BatchExecutor。

// 通过configure对象的newExxcutor方法,使用事务和执行器类型实例化一个Executor执行器对象
Executor executor = this.configuration.newExecutor(tx, execType);
package org.apache.ibatis.session;

public enum ExecutorType {
    SIMPLE,  
    REUSE,
    BATCH;

    private ExecutorType() {
    }
}
   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);
        }
        // 拦截器,configure的属性,在全局配置文件中设置
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

2. StatementHanlder

StatementHanlder数据库会话器,主要有三种实现类:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler,分别对应Executor的三种执行器(SIMPLE、REUSE、BATCH)

// 创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);


// -->调用configure的newStatementHandler方法

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

// -->根据执行器类型创建相应的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());
        }

    }

3. ParameterHandler

对预编译中参数进行设置,Executor会执行StatementHandler的prepare()方法进行预编译---->填入connection对象等参数---->再调用parameterize()方法设置参数---->完成预编译

// doUpdate、doQuery等方法中的关键代码
// 通过configure对象创建StatementHandler对象,由这个StatementHandler对象来预编译sql语句并执行数据库操作
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());



    // Executor会执行StatementHandler的prepare()方法进行预编译---->填入connection对象等参数---->再调用parameterize()方法设置参数---->完成预编译
    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;
    }
    // StatementHandler的基本实现类BaseStatementHandler实现prepare方法,只要是调用instantiateStatement方法根据jdbc连接创建statement对象
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

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

    // 以StatementHandler的实现类SimpleStatementHandler为例
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.createStatement() : connection.createStatement(this.mappedStatement.getResultSetType().getValue(), 1007);
    }
    // 调用PreparedStatementHandler的parameterize方法添加参数, 通过DefaultParameterHandler的setParameters方法从ParameterObject中取出参数,交给typeHandler处理
    public void parameterize(Statement statement) throws SQLException {
        this.parameterHandler.setParameters((PreparedStatement)statement);
    }
    //调用DefaultParameterHandler的setParameters
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (this.boundSql.hasAdditionalParameter(propertyName)) {
                        value = this.boundSql.getAdditionalParameter(propertyName);
                    } else if (this.parameterObject == null) {
                        value = null;
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (SQLException | TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    }
                }
            }
        }

    }

4. ResultSetHandler

结果集处理器,对jdbc返回的结果集封装,调用DefaultResultSetHandler的handleCursorResultSets,将查询结果集封装为list+分页等信息返回

    // PreparedStatementHandler的query方法
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        // 预编译sql并执行
        ps.execute();
        // resultSetHandler对jdbc返回的结果集封装
        return this.resultSetHandler.handleResultSets(ps);
    }
    // DefaultResultSetHandler的handleCursorResultSets,将查询结果集封装为list+分页等信息返回
    public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling cursor results").object(this.mappedStatement.getId());
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);
        if (resultMapCount != 1) {
            throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
        } else {
            ResultMap resultMap = (ResultMap)resultMaps.get(0);
            return new DefaultCursor(this, resultMap, rsw, this.rowBounds);
        }
    }

简述sqlsession查询的运行过程,Executor执行器执行doQuery方法,假设SIMPLE模式

2. 创建StamentHandler数据库会话器执行预编译(调用SimpleExecutor的prepareStatement方法);

2. prepare 方法负责生成 Statement 实例对象(通过SimpleStatementHandler的instantiateStatement方法);

3. parameterize 方法用于处理 Statement 实例多对应的参数(通过DefaultParameterHandler的setParameters方法);

4. StamentHandler数据库会话器执行PreparedStatementHandler的query方法(jdbc的包下的preparedStatement的execute);

5. resultSetHandler返回了封装的结果集(通过DefaultResultSetHandler的handleCursorResultSets方法)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值