zoukankan      html  css  js  c++  java
  • Mybatis源码解析4——SqlSession

    上一篇文章中,我们介绍了 SqlSessionFactory 的创建过程,忘记了的,可以回顾一下,或者看下下面这张图也行。

    image-20210722082105477

    接下来,可乐讲给大家介绍 Mybatis 中另一个重量级嘉宾——SqlSession,有了这个对象,我们就能对数据进行一顿操作了。大家伙小板凳搬起来,请看可乐为大家一一道来。

    搬好小板凳

    1、实例代码

    在实例搭建文章中,通过 SqlSession 对象查询数据,可乐写了两种方法。

    ①、常规的需要我们拼接 statement 方式;

    ②、xxxMapper.interface 接口代理方式;

    image-20210723075116982

    对应下面两种方法:

    //根据id查询person表数据
    @Test
    public void testSelectPersonById() {
        /*这个字符串由 PersonMapper.xml 文件中 两个部分构成
            <mapper namespace="com.itcoke.mapper.PersonMapper"> 的 namespace 的值
            <select id="selectPersonById" > id 值
        */
        String namespace = "com.itcoke.mapper.PersonMapper";
        String method = "selectPersonById";
        //根据 sqlSessionFactory 产生 session
        SqlSession sqlSession = sessionFactory.openSession();
        Person person = sqlSession.selectOne(namespace + "." + method, 1L);
        System.out.println(person);
        sqlSession.close();
    }
    
    
    //根据id查询person表数据
    //通过接口代理的方式
    @Test
    public void testInterfaceSelectPersonById() {
        //根据 sqlSessionFactory 产生 session
        SqlSession sqlSession = sessionFactory.openSession();
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        Person person = mapper.selectPersonById(1L);
        System.out.println(person);
        sqlSession.close();
    }
    

    本篇文章可乐讲给大家介绍第一种原生方式,其实第二种接口代理最后也是走的第一种方式,这个我们下篇文章具体介绍。

    2、构建过程图示

    image-20210815211833789

    3、代码剖析

    3.1 Executor

    我们通过 DefaultSessionFactory.openSession() 方法获取 sqlSession

    image-20210726222837613

    其实是可以通过构造方法指定 Executor 的类型,比如:

    SqlSession sqlSession = sessionFactory.openSession(ExecutorType.SIMPLE);
    

    再看生成 Executor 的源代码:

    image-20210726223115962

    如果不指定执行器类型,直接默认 openSession() 方法,生成的是 CachingExecutor 执行器,这里的 cacheEnabled 其实是默认开启二级缓存的配置,在 mybatis-config.xml 文件中.

    并且需要注意的是这里 new CachingExecutor(executor),传进去了一个 SimpleExecutor 对象,后面和数据库交互的实际上是该对象。

    image-20210726224122488 image-20210726223429105

    得到了 SqlSession,接下来看这段代码:

    Person person = sqlSession.selectOne(namespace + "." + method, 1L);
    

    image-20210726224337803

    直接看上面源码的第 76 行代码:

    List<T> list = this.selectList(statement, parameter);
    

    image-20210726224535733

    在上一篇文章介绍 SqlSessionFactory 的构建过程时,我们说了 configuration 对象的组成:

    image-20210722222701374

    看上面的源码得到 MappedStatement 对象,包含了我们在 mapper.xml 文件中配置的 sql 语句。

    image-20210727073353475

    执行 executor.query() 方法,注意,这里的 executor 是 CachingExecutor:

    image-20210727073829137

    这段源码,我们可以得到两个信息:

    ①、获取我们指定配置的boundSql 对象,包含我们配置的 sql 语句和参数信息。

    ②、根据相关信息得到一个缓存 key,通过这个key,连续两次相同的查询,第二次可以不去查数据库,直接获取缓存的数据。

    接着我们继续看 query() 方法:

    image-20210727074520738

    看源码,也就是说先去查缓存,缓存命中了直接返回数据,没有命中就执行:delegate.query() 方法。

    这里的 delegate 是上文我们说的构造 Executor 传进来得 SimpleExecutor 对象。

    image-20210727080139697

    关于 mybatis 缓存后面可乐会专门写一篇文章来仔细介绍,这里我们先梳理主线,看上面第 156 行代码,缓存查不到,从数据库里面查。

    继续跟 queryFromDatabase() 方法:

    image-20210727080523287

    3.2 StatementHandler

    执行到上面第 61 行源码:

    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    

    跟进去:

    image-20210727082254753

    这里构造的是一个 RoutingStatementHandler 对象,聪明的你一听听名字都知道会路由生成别的对象。

    没错,是根据传入的 statementType 生成具体的对象:

    image-20210727082515553

    在 MappedStatement 对象中,默认 statementType 是 PERPARED:

    image-20210727082829357

    也就是这里最终生成的 StatementHandler 实际上是 PreparedStatementHandler 对象。

    通常情况下:

    ①、不带参数的 SQL语句执行,会生成 SimpleStatementHandler 对象。

    ②、带参数的 SQL 语句执行,会生成 PreparedStatementHandler 对象。

    ③、存储过程执行,会生成 CallableStatementHandler 对象。

    3.3 ParameterHandler

    再回到 SimpleExecutor 的 doQuery() 方法:

    image-20210727080523287

    看第 62 行代码:

    stmt = prepareStatement(handler, ms.getStatementLog());
    

    跟进去:

    image-20210729231618083

    这里第 86 行获取数据库连接。

    第 88 行,进行参数处理,说直接点,就是将 SQL 语句中的 “?” 替换成我们传入的具体值。

    但是我们知道 Java对象参数和数据库参数是不一样的,那么肯定还会做参数类型转换,没错,就是通过下面将要介绍 TypeHandler 来完成。

    3.4 TypeHandler

    也就是将 Java 类型和 数据库类型进行互相转换。

    image-20210729230021702

    Mybatis 提供给了很多内置的参数转换,基本上不需要我们自己去定义。

    image-20210728221922904

    当然,聪明的你可能会问了,假如这些都不满足呢?假如我要自定义一些类型呢?

    不用担心,Mybatis 给我们预留了自定义类型的接口,如果你想自定义类型,通常分为两步:

    ①、实现 org.apache.ibatis.type.TypeHandler 接口, 或继承类 org.apache.ibatis.type.BaseTypeHandler

    ②、在配置文件中配置自定义的 TypeHnadler。

    这里可乐就不做详细描述了,官网写的也很清楚:

    https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers

    3.4 ResultSetHandler

    还是回到 SimpleExecutor 的 doQuery() 方法:

    上一步我们得到了 PrepraedStatementHandler 对象,接着看 handler.query() 方法:

    image-20210727083626964

    通过执行 ps.execute() 方法,得到结果集,然后通过 resultSetHandler 去处理结果集。

    4、总结

    这样,可乐就给大家完整的讲解了如何通过 SqlSession 进行一次数据库查询操作,但是正如文章开头所言,可乐给大家介绍了两种查询方式,一种是需要自己拼接 namespace+method 语句,另一种是通过 接口的形式。其实这两种方式是一样的,namespace 就是接口的包名(对应xxxMapper.xml 的namespace名称),method 就是接口的方法名(对应到xxxMapper.xml 的每个SQL语句的 id 属性),通过两者拼接,我们能去 xxxMapper.xml 文件中定位到具体的 SQL 语句。

    很明显,通过接口的形式避免了我们写字符串错误的可能,实际开发中,我们基本上都是这种方式,下一篇文章,可乐将给大家揭秘这种方式的奥秘。

    作者:IT可乐

    资源:微信搜【IT可乐】关注我,回复 【电子书】有我特别筛选的免费电子书。
    本文版权归作者所有,欢迎转载,但未经作者同意不能转载,否则保留追究法律责任的权利。
  • 相关阅读:
    代理模式和策略模式的区别
    代理模式vs适配器模式vs外观模式 转
    装饰模式与代理模式的区别(转载)
    用Delphi实现动态代理(2):设计说明  转
    Java静态内部类的介绍
    非常好的Java反射例子
    Java多线程中的锁机制
    Java多线程共享数据、同步、通信
    Java程序运行超时后退出解决
    Java安全:运用加密技术保护Java源代码
  • 原文地址:https://www.cnblogs.com/ysocean/p/15220922.html
Copyright © 2011-2022 走看看