mybatis

本文深入讲解MyBatis框架,涵盖其工作原理、架构设计、缓存机制等内容,解析MyBatis如何通过映射配置文件暴露SQL语句,实现数据库操作。

1.mybayis理解:

mybatis是一个暴露出sql语句的jdbc封装,是一个暴露sql语句的持久层框架;暴露的方式是映射配置文件;

2.mybatis流程:

mybatis整个调用执行过程:

  1. 加载配置并初始化:即mybatis主配置文件,mapper配置文件及注解配置。其中将SQL配置的信息加载为一个个MapperStatement对象(传入参数映射配置、执行的SQL语句、结果映射配置)。
  2. 接收调用请求:接收到传入的参数和需要执行的SQL的ID,将请求传递给下层的数据处理层进行处理。
  3. 处理操作请求:执行器Executor处理接收到接口层传递的SQL的ID和传入参数,根据ID查找对应的MapperStatement,解析MapperStatement对象,得到需要执行的SQL语句并注入传入的参数。获取到数据库连接,将最终的SQL语句到数据库执行,并得到结果。根据MapperStatement对象中的结果映射配置对得到的结果进行转换处理,得到最终的结果。最后释放资源并返回结果到上层。

PS:Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应 一个Mapped Statement对象,sql的id即是Mapped statement的id。

3.mybatis整体架构设计分为三层:

API接口层、数据处理层、基础支持层。

  1. API接口层:提供外部使用接口的API,通过提供的API操作数据库。接口层收到调用参数后就会将这些参数作为入参数调用数据处理层来完成具体的数据处理。
  2. 数据处理层:接收到接口层的参数,具体负责对SQL的查找,解析,执行和执行结果的映射处理,完成对数据库的操作。
  3. 基础支持层:负责最基础且公用的功能支持,包括数据库连接管理、事务管理、配置加载和缓存处理。这些功能抽取出来作为基础功能,为上层的数据处理层提供最基础的支撑。

4.mybatis的一级缓存,二级缓存

在这里插入图片描述

①一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

②二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

1.一级缓存

①我们在一个 sqlSession 中,对 User 表根据id进行两次查询,查看他们发出sql语句的情况。

@Test
public void testSelectOrderAndUserByOrderId(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession = sessionFactory.openSession();
    String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper.selectUserByUserId(1);
    System.out.println(u1);
     
    //第二次查询,由于是同一个sqlSession,会在缓存中查找查询结果
    //如果有,则直接从缓存中取出来,不和数据库进行交互
    User u2 = userMapper.selectUserByUserId(1);
    System.out.println(u2);
     
    sqlSession.close();
}

查看控制台打印情况:
在这里插入图片描述

② 同样是对user表进行两次查询,只不过两次查询之间进行了一次update操作。

@Test
public void testSelectOrderAndUserByOrderId(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession = sessionFactory.openSession();
    String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID";
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper.selectUserByUserId(1);
    System.out.println(u1);
     
    //第二步进行了一次更新操作,sqlSession.commit()
    u1.setSex("女");
    userMapper.updateUserByUserId(u1);
    sqlSession.commit();
     
    //第二次查询,由于是同一个sqlSession.commit(),会清空缓存信息
    //则此次查询也会发出 sql 语句
    User u2 = userMapper.selectUserByUserId(1);
    System.out.println(u2);
     
    sqlSession.close();
}

控制台打印情况:
在这里插入图片描述

③、总结

1、第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

2、如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

3、第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
在这里插入图片描述

2.二级缓存

二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去缓存中取。但是一级缓存是基于 sqlSession 的,而 二级缓存是基于 mapper文件的namespace的,也就是说多个sqlSession可以共享一个mapper中的二级缓存区域,并且如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中。

在这里插入图片描述

1.二级缓存的使用

①开启二级缓存

和一级缓存默认开启不一样,二级缓存需要我们手动开启

首先在全局配置文件 mybatis-configuration.xml 文件中加入如下代码:

<!--开启二级缓存  -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

其次在 UserMapper.xml 文件中开启缓存

<!-- 开启二级缓存 -->
<cache></cache>

总结:我们可以看到 mapper.xml 文件中就这么一个空标签,其实这里可以配置,PerpetualCache这个类是mybatis默认实现缓存功能的类。我们不写type就使用mybatis默认的缓存。也可以自己去实现 Cache 接口来自定义缓存,自定义缓存类必须实现Cache接口。

在这里插入图片描述

在这里插入图片描述

由图可以看到 二级缓存 底层还是 HashMap 架构。

②po 类实现 Serializable序列化接口
在这里插入图片描述

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接口。

③测试

一、测试二级缓存和sqlSession 无关

@Test
public void testTwoCache(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession1 = sessionFactory.openSession();
    SqlSession sqlSession2 = sessionFactory.openSession();
     
    String statement = "com.ys.twocache.UserMapper.selectUserByUserId";
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper1.selectUserByUserId(1);
    System.out.println(u1);
    sqlSession1.close();//第一次查询完后关闭sqlSession
     
    //第二次查询,即使sqlSession1已经关闭了,这次查询依然不发出sql语句
    User u2 = userMapper2.selectUserByUserId(1);
    System.out.println(u2);
    sqlSession2.close();
}

可以看出上面两个不同的sqlSession,第一个关闭了,第二次查询依然不发出sql查询语句,直接到二级缓存中查询。

二、测试执行 commit() 操作,二级缓存数据清空

@Test
public void testTwoCache(){
    //根据 sqlSessionFactory 产生 session
    SqlSession sqlSession1 = sessionFactory.openSession();
    SqlSession sqlSession2 = sessionFactory.openSession();
    SqlSession sqlSession3 = sessionFactory.openSession();
     
    String statement = "com.ys.twocache.UserMapper.selectUserByUserId";
    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper userMapper3 = sqlSession2.getMapper(UserMapper.class);
    //第一次查询,发出sql语句,并将查询的结果放入缓存中
    User u1 = userMapper1.selectUserByUserId(1);
    System.out.println(u1);
    sqlSession1.close();//第一次查询完后关闭sqlSession
     
    //执行更新操作,commit()
    u1.setUsername("aaa");
    userMapper3.updateUserByUserId(u1);
    sqlSession3.commit();
     
    //第二次查询,由于上次更新操作,缓存数据已经清空(防止数据脏读),这里必须再次发出sql语句
    User u2 = userMapper2.selectUserByUserId(1);
    System.out.println(u2);
    sqlSession2.close();
}

查看控制台情况:

在这里插入图片描述

总结:二级缓存也是跟一级缓存一样,

1、第一次发起查询时,先去找二级缓存中是否有信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到二级级缓存中。

2、如果中间任何sqlSession去执行commit操作(执行插入、更新、删除),则会清空二级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

3、第二次发起查询用户信息时,先去找缓存中是否有用户信息,缓存中有,直接从缓存中获取用户信息。

④useCache和flushCache

mybatis中还可以配置userCache和flushCache等配置项(在mapper.xml里配置),userCache是用来设置是否禁用二级缓存的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="selectUserByUserId" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
    select * from user where id=#{id}
</select>

这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数据库中获取。

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

设置statement配置中的flushCache=”true” 属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
    select * from user where id=#{id}
</select>

一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。所以我们不用设置,默认即可。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值