Mybatis

在现代软件开发中,数据持久化是一个至关重要的环节。为了方便和高效地访问数据库,ORM框架应运而生。而MyBatis作为一款优秀的ORM框架,不仅提供了强大的数据库访问能力,更注重SQL的灵活性和可维护性。本文将带你深入了解MyBatis的原理,了解它是如何实现高效、灵活的数据访问的。

目录

执行流程

读取配置文件

操作数据库

延迟加载

什么是延迟加载?

延迟加载原理

 一二级缓存

一级缓存

二级缓存

注意事项


执行流程

 

读取配置文件

首先Mybatis会读取配置文件mybatis-config.xml,这之中有两部分内容,一个是当前的环境配置,指定使用的数据库,用户名密码等等。第二个是加载一些映射文件,比如UserMapper。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- 数据库连接信息 -->
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
        <property name="username" value="root"/>
        <property name="password" value="mypassword"/>
      </dataSource>
    </environment>
  </environments>

  <!-- 映射器的配置 -->
  <mappers>
    <mapper resource="com/example/mappers/UserMapper.xml"/>
  </mappers>
</configuration>

操作数据库

Mybatis操作数据库主要是通过一个SqlSession对象完成,所以下一步需要创建SqlSession,而SqlSession有专门的对象进行创建,也就是图中的SqlSessionFactory,这是一个构建绘画的工厂,全局只有一个,可以批量生成SqlSession。

Mybatis会通过这个SqlSession工厂来创建SqlSession,最终这个SqlSession会包含所有的执行sql语句的方法,SqlSession才是真正和数据库打交道的对象,并且每一次操作都有一个会话,SqlSession是有很多个的。

下面的Executor是真正操作数据库的接口,封装这jdbc的一系列操作,这里面同时也维护了一些缓存(比如一级缓存和二级缓存)。Executor会从MappedStatement对象在读取一些数据库的信息。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mappers.UserMapper">
  <select id="selectById" resultType="User">
    SELECT * FROM users WHERE id = #{id}
  </select>
</mapper>

在这个样例映射器文件中,你需要根据你自己的数据库和实体类进行相应的修改。以下是对一些关键配置项的说明:

  1. namespace 属性指定了映射器接口的完全限定名,这里假设为 com.example.mappers.UserMapper。你需要根据实际情况修改为你自己的接口路径。
  2. <select> 元素定义了一个查询语句。id 属性指定了方法名,这里是 selectByIdresultType 属性指定了查询结果的类型,这里是 User 类型。
  3. 在 <select> 元素的内容中,可以编写具体的 SQL 查询语句。这个示例中,通过 SELECT * FROM users WHERE id = #{id} 查询指定ID的用户数据。#{id} 是一个占位符,表示参数值。

标签中的这些信息都是由MappedStatement对象来读取存储的。在操作数据库之前,我们还需要进行数据类型的转换,不管是map,list,string还是integer,我们都需要转换为数据库支持的类型,类型转换完成才能去操作数据库。类型转换之后,还要把数据库类型的结果转换为java类型。

总结一下,mybatis的执行流程可以归纳为以下几步:

  1. 读取配置文件:mybatis-config.xml加载运行环境和映射文件
  2. 构造会话工厂SqlSessionFactory
  3. 会话工厂创建SqlSession对象(包含了执行sql语句的所有方法)
  4. 操作数据库的接口,Executor执行器,同时负责查询缓存数据的维护
  5. Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
  6. 输入参数映射
  7. 输出结果映射

延迟加载

先说结论,mybatis支持延迟加载,但是它默认是没有开启的。

什么是延迟加载?

延迟加载(Lazy Loading)是指在需要使用关联对象时才进行数据库查询,而不是在加载主对象时就立即查询所有关联对象的数据。

有两张表,用户表和订单表,用户和订单是一对多的关系,左侧是用户实体类 ,右边是订单的实体类。用户的实体类有一个成员变量叫做orderList,这里面封装的都是订单,也就是当前用户的订单列表。如果我们查询用户的时候就把用户的订单也查询出来,就属于立即加载。如果查询用户的时候,先不查询订单数据,而是当需要订单的时候再查询订单,就是延迟加载。

我们可以在mapper文件中添加属性fetchType=“lazy”来定义延迟加载的属性。

延迟加载原理

首先使用CGLIB创建目标对象的代理对象(orderList),主要是用代理对象来完成延迟加载。当我们调用目标方法(getOrderList),就会进入到代理对象的invoke方法,在这里去判断orderList是否为空,如果使用的是延迟加载,那么orderList肯定是空的,此时mybatis才回去真正的执行sql查询。拿到结果后再把结果封装到user中的属性(orderList)

 一二级缓存

我们回忆一下之前redis的缓存是怎么用的:

 

假如一个请求来了之后我们首先回去判断是否命中了缓存,如果缓存中没有数据我们才回去查询数据库,同时再把数据保存在缓存中,再返回给用户,这是缓存的基本使用方式。

mybatis是支持一级和二级缓存的。缓存都是保存在本地叫做本地缓存,有一个缓存类叫做PerpetualCache,本质是一个HashMap。一级和二级缓存都是基于PerpetualCache来做的。

一级缓存的作用域是session级别的,相对来说是比较小的。

二级缓存的作用域会更大些,是命名空间和mapper的作用域,不依赖于SqlSession。

这两种缓存的基本思路和上面redis缓存的思路大致一样,不同的是不同的作用域下保存数据的实际是不一样的,下面我们来相信的说明下一二级缓存的作用效果。

一级缓存

基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session进行flush或close之后,该session中所有cache就将清空,默认打开一级缓存。

 首先我们open了一个session,我们拿到了两个userMapper,如何使用userMapper1去查询用户id为6的数据,返回的是用户,然后用userMapper2也去查询用户为6的用户,此时这两条查询的是相同的id,这时一级缓存就会起作用了,因为第一次查询的时候他会把数据保存在本地的缓存中(hashMap),使用第二次查询的时候就不会再执行sql了。

二级缓存

二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于sqlSession,也是默认采用PerpetualCache,本质上都是存储到hashMap中。

这段代码中,我们打开了两次session,分别取查询id为6的数据 ,这两次查询属于不同的会话,最终的结果就是会执行两条SQL。

如果就想执行一次的话,就需要去开启我们的二级缓存了。开启二级缓存需要两步操作,因为而二级缓存默认是关闭的,我们需要手动打开。

首先要在全局配置文件中开启二级缓存:

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

然后再映射文件中加一个cache标签:

<select id="selectUserById" resultType="User" useCache="false">
    ...
</select>

注意事项

  1. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存nameSpaces)进行了增删改操作后,默认该作用域下所有select中的缓存将被清空。
  2. 二级缓存需要缓存的数据需要实现Serilizable接口
  3. 只有会话提交或关闭以后,一级缓存中的数据才会转移到二级缓存中

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值