Mybatis源码分析(1)-Mybatis 执行过程分析

一.整体执行过程

整体执行过程大致如上图所示。具体的步骤可以拆分成一下内容

1.首先解析配置,得到Configuration

2.创建SqlSession回话,用于和数据库完成交互。

3.SqlSession中创建不同的Executor执行程序。

4.Executor中创建StatementHandler来调用jdbc程序。

5.Executor中指定ParameterHandler来将java类型转化成jdbc类型,然后查询数据库,数据库返回的数据通过ResultSetHandler把jdbc类型转化成java类型。

 

二.具体执行过程分析

整体的执行过程的时序图如上图所示。

下面一起来分析整个执行过程

 

1.创建mybatis的环境

 

本次验证过程中,使用的mybatis的编程式方式调用

mybatis-config.xml文件配置如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="SLF4J"/>
        <!--<setting name="cacheEnabled" value="true" />-->
    </settings>
    <!--<plugins>-->
        <!--<plugin interceptor="com.gupaoedu.mybatis.plugins.TestPlugin">-->
        <!--</plugin>-->
    <!--</plugins>-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://172.16.159.211:3306/user"/>
                <property name="username" value="root"/>
                <property name="password" value="******"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="TestMapper.xml"/>
    </mappers>
</configuration>

pom依赖文件

 <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>
TestMapper接口
public interface TestMapper {

     User selectByPrimary(Integer id);
}
TestMapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace= "com.mybaties.demo.TestMapper">
  <!--<cache-->
          <!--eviction="FIFO"-->
          <!--flushInterval="60000"-->
          <!--size="512"-->
          <!--readOnly="true"/>-->
    <select id="selectByPrimary" resultType="com.mybaties.demo.User">
      select * from user where id   = #{id}
    </select>
</mapper>

测试类

    /**
     * 通过接口形式调用
     */
    @Test
    public void Test() throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("/Applications/study/java/freamework-study/mybaties/src/main/java/com/mybaties/demo/mybatis-config.xml");

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

        SqlSession sqlSession = sqlSessionFactory.openSession();

        TestMapper mapper = sqlSession.getMapper(TestMapper.class);

        User user = mapper.selectByPrimary(2);

        System.out.println(user.toString());
    }

 

2.开始调试代码分析

 

我们知道mabtis的调用方法是通过接口调用的,但是接口是不能被实例化的,那具体的调用是怎么的呢?

        TestMapper mapper = sqlSession.getMapper(TestMapper.class);

在程序调用上一端代码的时候,进入了MapperProxy的invoke方法,而且返回的是MapperProxy的代理对象,所以我们可以知道mybatis是 通过代理模式来调用接口中的方法的。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }

继续跟踪

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        switch(this.command.getType()) {
        case INSERT:
          ...
        case UPDATE:
          ...
        case DELETE:
          ...
        case SELECT:
            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 if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
            break;
        case FLUSH:
            ...
    }

由于我们调用的是select方法 ,那么 在将会调用sqlSession.selectOne的方法,selectOne方法的具体实现在selectList

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            ...
        }

        return var5;
    }

sqlSession找到对应的MappedStatement对象,然后委派给 executor来完成调用

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //拼接sql
        BoundSql boundSql = ms.getBoundSql(parameter);
        //拼装缓存key
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
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());
            ...

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

             ...
            return list;
        }
    }

queryFromDatabase调用doQuery方法

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

        List var10;
        try {
            this.flushStatements();
            Configuration configuration = ms.getConfiguration();
            //创建StatementHandler,在构造方法中初始化了ParameterHandler,ResultSetHandler
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
            Connection connection = this.getConnection(ms.getStatementLog());
            stmt = handler.prepare(connection, this.transaction.getTimeout());
            //将请求参数放入到查询sql
            handler.parameterize(stmt);
            var10 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var10;
    }

调用jdbc程序查询数据库,并将jdbc类型转成java类型,返回查询结果。

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        ps.execute();
        return this.resultSetHandler.handleResultSets(ps);
    }

 

3.总结

 

总体来说,mybatis的程序体系并不复杂。SqlSession代表一次完整的数据库回话,Executor代表一次查询动作,Executor通过委派StatementHandler来与数据库交互。StatementHandler又含有ParameterHandler和ResultSetHandler两个处理器,这两个处理器 完成jdbc类型和java类型之间的相互转换。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值