selectById的流程

  上一篇,我们讲过DAO层接口,是怎么被mybatis自动实现的。DAO层接口,为什么能操作数据库

这篇,我们继续下去,讲一讲DAO层接口中selectById的流程。

 

一、准备工作

  和上一篇文章中,用到的代码几乎没有区别,只是Main类有所调整

public class Main {

    public static SqlSession getSqlSession() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory.openSession();
    }

    public static void main(String[] args) throws IOException {
        TestDAO testDAO = getSqlSession().getMapper(TestDAO.class);
        Test test = testDAO.selectById(1);

        testDAO.testDefaultMethod();

        //类文件是缓存在java虚拟机中,我们将类文件打印到文件中,便于查看
//        generateProxyFile("F:/TestDAOProxy.class");
    }

    private static void generateProxyFile(String path){
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{TestDAO.class});

        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }
    }
}

 

二、开始debug

1、起始位置

   从main方法中的Test test = testDAO.selectById(1);这一行开始debug。探究selectById的流程

2、UML图

  这里先给出一个UML图,照着这个UML进行讲解。当然这里UML图太繁琐了,根本记不住,我们会在最后给出一个简化的UML图,方便理解记忆

selectById的流程_第1张图片

 

3、TestDAO的selectById方法,实际上是调用了它的代理类MapperProxy的invoke方法

selectById的流程_第2张图片

那么我们就来理一理这个invoke方法:

  1)Object.class.equals(method.getDeclaringClass()) 这句话的意思是:判断method这个方法,是不是Object类中的方法,比如没有在子类中被重写的toString()、hashCode()、equals()方法。

 2)isDefaultMethod(method) 的代码如下:

/**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return (method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
        && method.getDeclaringClass().isInterface();
  }

   我们可以看到这个判断语句,前半段的意思是:这个方法被public修饰,但是不被abstract、static修饰。后半段的意思是:这个方法是接口中的方法。

  又要是接口中的方法,又不能被abstract、static修饰,接口中真的有这种方法吗?

确实是有的,JDK8以后,接口可以有方法体

  

 

4、很明显selectById不是接口默认方法,那么我们只能走final MapperMethod mapperMethod = cachedMapperMethod(method);这一步了。

selectById的流程_第3张图片

从methodCache中get出一个MapperMethod。这个methodCache又是哪来的?

selectById的流程_第4张图片

我们回溯一下,发现methodCache,是从MapperProxy中的构造方法来的。那么看看谁调用了这个构造方法。

selectById的流程_第5张图片

好了,我们知道methodCache是自己new出来的。

 

5、很明显这个时候methodCache是空的,那么就要自己new 一个MapperMethod

selectById的流程_第6张图片

selectById的流程_第7张图片

其中,SqlCommand如下:

selectById的流程_第8张图片

 

6、MapperMethod已经被创建了,我们回到MapperProxy的invoke方法,发现下一步是调用MapperMethod的execute方法

selectById的流程_第9张图片

selectById的流程_第10张图片

  其中Object param = method.convertArgsToSqlCommandParam(args)是获取参数的值,也就是我们selectById(1)传进来的1。

 

7、继续下一步:result = sqlSession.selectOne(command.getName(), param);

selectById的流程_第11张图片

defaultSqlSession的selectOne,实际上是调用了selectList。那么我们继续跟踪selectList

selectById的流程_第12张图片

 

8、接下来调用了CachingExecutor类的query方法。那么这个CachingExecutor是什么时候被创建的?回溯一下,发现是在DefaultSqlSessionFactory的openSession方法中,被创建的。也就是我们代码中的这一句。

selectById的流程_第13张图片

我们已经知道了CachingExecutor是什么时候创建的,那么继续探索它的query方法

获取BoundSql和CacheKey之后,继续query

selectById的流程_第14张图片

又调用了SimpleExecutor的query方法,这里用到了装饰器模式(同宗同源)。

 

9、来到BaseExecutor的queryFromDatabase方法,终于调用了doQuery方法selectById的流程_第15张图片

 

10、在doQuery方法中,我们来看一下StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

selectById的流程_第16张图片

这句话,生成了3个handler,分别是StatementHandler、ParameterHandler、ResultSetHandler。后两个是作为StatementHandler的实例变量存在的。

selectById的流程_第17张图片

  这三个类的用法,可以参考官网,关于 插件 (plugins)的描述。

 顺道说一下,Executor、StatementHandler、ParameterHandler、ResultSetHandler这四个插件的生成顺序是 

Executor --> ParameterHandler  --> ResultSetHandler  --> StatementHandler。

 

11、生成3个handler之后,就来到了stmt = prepareStatement(handler, ms.getStatementLog());

selectById的流程_第18张图片

  这个方法的作用类似于我们使用JDBC时,获取连接,创建PreparedStatement,做一些前期准备。sql语句中的占位符也被传进来的参数替代了。

 

12、执行handler.query(stmt, resultHandler)

selectById的流程_第19张图片

selectById的流程_第20张图片

前面两句,是执行sql语句。

resultSetHandler. handleResultSets(ps)这句是处理返回结果,将ResultSet转换成我们需要的实体对象。

 

三、简化版的UML

这个步骤也太冗长了,我么简化一下,方便记忆

selectById的流程_第21张图片

 

 

你可能感兴趣的:(mybatis查询流程,selectById,mybatis,mybatis源码解析)