• MyBatis源码之MyBatis中SQL语句执行过程


    MyBatis源码之MyBatis中SQL语句执行过程

    SQL执行入口

    我们在使用MyBatis编程时有两种方式:

    方式一代码如下:

    SqlSession sqlSession = sqlSessionFactory.openSession();
    List<Student> studentList = sqlSession.selectList("com.sjdwz.dao.StudentMapper.findAll");
    
    • 1
    • 2

    方式二代码如下:

    SqlSession sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = studentMapper.findAll();
    
    • 1
    • 2
    • 3

    方式一入口分析

    方式一调用的是SqlSession接口的selectList方法:

    截图

    执行的是DefaultSqlSession中的实现

    截图

    经过多次重载后,调用的是此方法:

    截图

    MappedStatement封装的是我们的SQL语句;

    方法内部执行的是executor的query方法。

    方法二入口分析

    方法二调用的是SqlSession接口的getMapper(Class type)方法:

    截图

    我们实际调用的是DefaultSqlSession实现类中的方法:

    截图

    截图

    此方法内部又调用了

     configuration.<T>getMapper(type, this);
    
    • 1

    截图

    最后调用了:

    截图

    return mapperProxyFactory.newInstance(sqlSession);
    
    • 1

    通过工厂创建了接口的代理对象。

    Mapper接口动态代理

    上面讲到会通过

     mapperProxyFactory.newInstance(sqlSession);
    
    • 1

    来创建动态代理类;

    那创建动态代理类会执行哪些方法呢?

    截图

    可以看到有MapperProxy这个类,实际上都会执行MapperProxy类中的invoke方法。

    invoke方法

    invoke方法会先判断方法是不是Object的方法,如果是,就直接调用;否则会执行cachedInvoker()方法

    cachedInvoker()的作用是获取缓存中MapperMethodInvoker,如果没有则创建一个,而MapperMethodInvoker内部封装了MethodHandler。

    cachedInvoker

    当cacheInvoker返回了PalinMethodInvoker实例之后,紧接着调用了这个实例的PlainMethodInvoker#invoke方法

    PlainMethodInvoker

    然后就调用了execute()方法

    execute方法

    里面调用了sqlSession的方法。

    方法的调用关系如下:

    MapperProxy#invoke()//代理对象执行的方法,代理后所有Mapper的方法调用时,都会调用这个invoke方法
    	MapperProxy#cachedInvoker()//代理对象里的Method也是对象,为了避免频繁new对象,在这里给Method对象加缓存
    		methodCache#computeIfAbsent()//从缓存中取Method对象,取不到创建之后加入缓存
            //PainMethodInvoker是MapperProxy的内部类
            PlainMethodInvoker#invoke()//执行方法,所有代理对象的方法都会执行同一个。代理对象本质是方法的拦截器!
    			MapperMethod#execute()//执行方法,最终还是在调用SqlSession接口
                    SqlSession#insert()
                    SqlSession#update()
                    SqlSession#delete()
                    SqlSession#selectOne()
                    SqlSession#selectList()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    SQL执行流程

    查询SQL执行流程

    主要步骤:

    1. selectOne/selectList
    2. SQL获取
    3. 参数设置
    4. SQL执行
    5. 封装结果集

    调用关系如下:

    DefaultSqlSession#selectOne()//执行单记录
    DefaultSqlSession#selectList()//查询列表
        CachingExecutor#query()
    		MappedStatement#getBoundSql()//获取SQL语句
    	CachingExecutor#query()//二级缓存查询
    		CachingExecutor.delegate = SimplyExecutor//装饰设计模式:Caching的对SimpleExecutor查询加二级缓存装饰
    	SimplyExecutor#query()
        	BaseExecutor#query()//子类执行查询直接调用父类方法,一级缓存
            BaseExecutor#queryFromDatabase()//缓存没有则去查询数据库,一级缓存
    	SimplyExecutor#doQuery()//simple执行查询
    		SimplyExecutor#prepareStatement()//准备查询语句
    			RoutingStatementHandler#parameterize()//路由delegate=PreparedStatementHandler
    				PreparedStatementHandler#parameterize()//设置查询参数
    				DefaultParameterHandler#setParameters()//设置查询参数
    			RoutingStatementHandler#query()//路由delegate=PreparedStatementHandler
    				PreparedStatementHandler#query()//执行SQL,封装结果集
        				PreparedStatement#execute()//执行SQL查询操作
    					DefaultResultSetHandler#handleResultSets()//封装返回值,将查询结果封装成Object对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    增删改SQL执行流程

    主要步骤

    1. insert|update|delete方法分析
    2. SQL获取
    3. 参数设置
    4. SQL执行
    5. 封装结果集

    MyBatis中增删改的代码如下:

    //DefaultSqlSession
    @Override
    public int insert(...) {
    return update(statement, parameter);
    }
    @Override
    public int update(String statement) {
    return update(statement, null);
    }
    @Override
    public int delete(...) {
    return update(....);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们发现,增删改最后执行的都是update,这是因为insert、update、delete实际上都是对数据库中数据的改变

    执行流程为:

    DefaultSqlSession#update()
    	CachingExecutor#update()//执行更新
    		flushCacheIfRequired()//执行增删改前,清除对应二级缓存
    		CachingExecutor.delegate = SimplyExecutor//装饰模式
    	SimplyExecutor#update()//调用父类模板方法
    		BaseExecutor#update()//执行更新
    			BaseExecutor#clearLocalCache()//清除一级缓存,LocalCache
    		    BaseExecutor#doUpdate()//调用子类方法
    	SimplyExecutor#doUpdate()//simple执行更新
    		SimplyExecutor#prepareStatement()//准备查询语句
    			RoutingStatementHandler#parameterize()//delegate=PreparedStatementHandler
    				PreparedStatementHandler#parameterize()//设置查询参数
    				DefaultParameterHandler#setParameters()//设置查询参数
                RoutingStatementHandler#update()//delegate=PreparedStatementHandler
                    PreparedStatementHandler#update()
                        PreparedStatement#execute()//执行SQL,完成增删改查操作
                        reparedStatement#getUpdateCount()//获取影响行数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    图示

    最后我们画出执行的流程图如下:

    执行流程图

  • 相关阅读:
    2. selenium学习
    使用Hybrid Flow并添加API访问控制
    解决Vue项目的runtime-only转为runtime-compiler
    Allegro中导入中文或者logo操作指导
    鸿运主动安全监控云平台任意文件下载漏洞复现 [附POC]
    【预处理详解】
    [应用推荐]Web Scraper——轻量数据爬取利器
    通讯网关软件005——利用CommGate X2OPC实现OPC客户端访问MS SQL服务器
    iOS描述文件(.mobileprovision)一键申请
    CSS常见选择器
  • 原文地址:https://blog.csdn.net/qq_41243472/article/details/138204153