mybatis实现原理解析(动态sql、动态代理、缓存等分析)

一、mybatis操作数据库基本流程

   1、读取配置文件信息
    InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
    2、获取连接工厂
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resource);
    3、打开连接
    SqlSession session = sessionFactory.openSession();
    4、执行查询,更新,插入,删除等操作
    List<User> users = session.selectList("com.letsiot.dao.UserMapper.findById","4");
    User one = session.selectOne("com.letsiot.dao.UserMapper.findById", "3");
    session.insert("com.letsiot.dao.UserMapper.save",user);
    session.update("com.letsiot.dao.UserMapper.updateById",user);
    5、如果是保存、修改、删除等操作需要提交事务
    session.commit();
    6、关闭连接
    session.close();

二、执行流程简单分析

    1、读取配置文件
    InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");
    经过应用类加载器加载把配置文件以刘的形式加载到内存。需要建立服务器与磁盘之间的socket连接。
    2、获取连接工厂
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resource);
    通过构建一个XML解析器解析配置文件,获取配置信息以及sql映射文件,把第一步获取的文件流解析到Configuration内,传递给工厂构建器
    3、打开一个连接
     SqlSession session = sessionFactory.openSession();
     指定session执行sql方式,事务隔离级别,是否自动提交等信息。
     4、执行查询
     执行查询又分为:
     4.1执行参数设置
     参数类型处理(参数区分为collection,list,array和其他)
     offset,limit偏移处理(默认offset为0,limit为Integer.MAX_VALUE)
     结果类型处理器设置,默认没有处理器
     4.2结果查询
     是否从缓存中获取还是从数据库查询
     4.3 mybatis的PreparedStatement执行查询操作,动态调用method.invoke()执行,最终会调用到本地方法
     4.4 mybatis的ResultSetHandler使用反射技术对结果集映射

三、mybatis解析动态sql分析

1、mybatis在通过SQLSessionFactoryBuilder构建SqlSessionFactory的时候会解析配置文件,首先解析配置文件根节点信息到XNode中。
1.1 解析sqlMapConfig.cml文件
在这里插入图片描述
1.2 从根节点开始已解析,解析成XNode对象。
在这里插入图片描述
1.3 逐个解析配置项,找到mapper节点。
在这里插入图片描述
1.4 根据mapper节点类型使用解析
在这里插入图片描述
配置package,最终将使用会使用MapperAnnotationBuilder解析器,否则将使用XMLMapperBuilder解析。
1.5 以XMLMapperBuilder为例分析
在这里插入图片描述

顺便可以看到sql映射文件必须要有namespace。
mybatis会解析cache-ref,cache标签,这两个标签时为了创建二级缓存使用。
接着mybatis会寻找parameterMap,resultMap,sql片段,select,delete,update,insert等。
1.6 解析select,delete,update,insert标签时会解析动态sql
跟踪XMLStatementBuilder的parseStatementNode方法,
在这里插入图片描述
mybatis会把标签内容解析到SqlSource中。
在这里插入图片描述
接下来mybatis会真正开始已解析配置文件,根据select标签中具体id,parameter,动态标签if,where等构造出不同的节点存入SqlSource中。
在这里插入图片描述
select * from user where 1=1
< if test=“id!=null and id!=’’”>and id=#{id}< /if>
< if test=“username!=null and username!=’’”>and username=#{username}< /if >
解析结果如下:
在这里插入图片描述
SqlSource实现类如下:
在这里插入图片描述

最终把SqlSource是否批处理,参数类型,返回类型等设置到configuration中。至此,mybatis解析工作才算完成。
在这里插入图片描述

四、SQLSession接口生成代理对象(动态代理)

代码:

UserMapper是一个接口,没有实现类,session.getMapper(User.class);会生成UserMapper接口的代理对象。
在这里插入图片描述
调用MapperProxyFactory创建一个对象。跟进newInstance(sqlSesion)方法内部。
在这里插入图片描述
可以看到mybatis使用的是jdk的动态代理创建的代理对象。
同时可以看到mapperProxyFactory来自于knownMappers的map中,在解析配置文件时候放入。
在这里插入图片描述
在这里插入图片描述可以看到解析完sql映射文件后都会把对应的Mapper接口和相关信息存入knownMappers。而且在加载解析失败后会把对应的Mapper从knownMappers中移除。

五、mybatis缓存

缓存是一般ORM框架都会提供的功能,目的是提高查询效率和较少数据库压力。mybatis相关的类都在cache包里,有一个默认实现的类PerpetualCache,使用HashMap结构。
1.一级缓存
1.1 mybatis一级缓存是SqlSession级别的缓存,默认开启。Mybatis开启一次和数据库的对话,会创建出一个SqlSession对象表示一次会话。Mybatis每一次查询都会从本地缓存中获取,如果不存在,就会从数据库查询,并把结果存入本地缓存。可以跟踪代码查看。
1.2 测试用例如下:
在这里插入图片描述
1.3 查询会使用DefaultSqlSession的selectList()方法,进而使用Executor的query()方法。
在这里插入图片描述
1.4 可以看到先创建缓存key,再调用查询方法。
在这里插入图片描述
1.5 此处会先从缓存中查找,如果找到就返回结果,找不到就调用queryFromDatabase()从数据库查询,查找的key就是上一步使用hash算法构建的key值。
1.6 跟踪queryFromDatabase()方法处理过程。
在这里插入图片描述
queryFromDatabase()就是从数据库查询,并把结果保存到缓存中。
1.7 如果是update,insert,delete操作则会清空缓存
在这里插入图片描述
BaseExecutor的update()方法可以看到,更新之前会先清除本地缓存,跟进insert和delete内部可以发现,最终都是update方法。
1.8 mybatis一级缓存小结

  • 对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果​。
  • 如果没命中,回去数据库查询,并把结果以key,value形式存到Cache中。
  • update,insert,delete操作会清空一级缓存。
  • 如果一级缓存是statement级别的,那么每次查询结束都会清掉一级缓存,每次都会从数据库查询。
    在这里插入图片描述
    设置方式如下:
    在这里插入图片描述

问题:

  • 一级缓存不能跨会话共享,不同的会话对于相同的数据可能会有不同的缓存。
    解决方案:

  • 将一级缓存设置为statement级别,这样就没有使用缓存。
    在这里插入图片描述

  • 使用中间件,缓存不同session的数据。
    在这里插入图片描述

  • 使用mybatis的二级缓存。
    2.二级缓存
    2.1配置
    需要在对应的mapper.xml文件中开启。
    在这里插入图片描述
    对应的pojo类需要实现序列化接口。
    在这里插入图片描述
    可以看到查询时候会先从二级缓存中获取,如果二级缓存中没有回调用query方法,query方法又会先从一级缓存中获取,如果没有查到才会查询数据库。
    2.2 二级缓存什么时候创建
    解析配置文件时候已经分析过,会在解析sqlMapConfig.xml配置文件时解析到mapper节点时会解析二级缓存相关标签。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    最终会把二级缓存标签解析放到Configuration对象中。
    2.3 insert,update,delete操作会清除二级缓存
    insert,delete,update操作最终都会调用CacheingExecutor的update()方法,update方法都会先执行刷新缓存操作。
    在这里插入图片描述
    在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值