Mybatis框架教程(史上最全图文详解)

Mybatis框架教程(史上最全图文详解)-mikechen

Mybatis的定义

MyBatis 是 Java 生态中非常著名的一款 ORM 框架,目前在一线互联网大厂中应用广泛,MyBatis已经成为了一个必会框架。

 

Mybatis的作用

解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。

提高了可维护性:因为SQL和代码的分离,sql写在xml里,便于统一管理和优化。

 

Mybatis与Hibernate的区别

1.SQL优化对比

Hibernate不需要编写大量的 SQL就可以完全映射,此外还提供 HQL(Hibernate Query Language)对 POJO 进行操作,查询时会查询所有字段,会有不必要的性能消耗。

MyBatis手动编写 SQL,所以调整方便,是针对响应的场景设计的 SQL,更灵活、 可控性更好、 更优化,特别适合很多大型网站需要上线检查SQL语句的场景。

 

2.开发速度对比

Hibernate的学习成本要比MyBatis高,MyBatis框架相对容易上手。

比如:

一个项目中用到最基本的增删改查,这样选择Hibernate的效率就要高些了,因为基本的SQL语句已经被封装好,可以节省大量时间。

但是对于一个大型项目复杂的查询语句较多,使用MyBatis就会加快很多,而且SQL语句管理也会很方便。

 

3.优劣势对比

Hibernate 优势

  1. Hibernate的DAO层开发比MyBatis简单,MyBatis 需要维护 SQL 和结果映射;
  2. Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL;
  3. Hibernate 有更好的二级缓存机制,可以使用第三方缓存,MyBatis 本身提供的缓存机制不佳;

 

MyBatis优势

  1. MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段;
  2. MyBatis 容易掌握,而 Hibernate 门槛较高;

 

4.开发方面对比

MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO、SQL 和映射关系;

Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可;

 

Mybatis的原理

MyBatis原理机制详解(附8大执行流程图解)

MyBatis原理如下图所示 :

Mybatis框架教程(史上最全图文详解)-mikechen

上面中流程就是MyBatis内部核心流程,一共会经历8大步骤,下面我会详解8大步骤。

第一步:读取MyBatis配置文件

MyBatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。

如下所示:

<configuration>
    <!-- 和spring整合后 environments配置将废除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理 -->
            <transactionManager type="JDBC" />
            <!-- 数据库连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
       <mapper  resource="sqlMapper/userMapper.xml"/>
    </mappers>
</configuration>

mapper.xml:sql的映射文件,配置了操作数据库的sql语句,此文件需在config.xml中加载。

比如:加载用户映射信息UserMapper.xml。

<mappers> 
  <mapper resource="sqlMapper/userMapper.xml"/> 
</mappers>

 

第二步:加载映射文件

SQL的映射文件,配置了操作数据库的SQL语句,需要在MyBatis配置文件MyBatis-config.xml中加载。

MyBatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

如下所示:

<mappers> 
  <mapper resource="sqlMapper/userMapper.xml"/> 
</mappers>

MyBatis-config.xml 加载用户表,映射文件为:UserMapper.xml。

 

第三步:构造会话工厂

通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory,用来开启SqlSession。

SqlSessionFactory是MyBatis框架中的一个接口,主要负责MyBatis框架初始化操作及为开发人员提供SqlSession对象。

它有两个实现类:DefaultSqlSessionFactory和SqlSessionManager,其中SqlSessionManager已被废弃。

在Mybtis初始化的过程中首先会读取MyBatis的核心配置文件,一般创建SqlSessionFactory的代码如下:

//使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory
SqlSessionFactory SqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。

 

第四步:创建会话对象

由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法,SqlSession对象完成和数据库的交互。

SqlSession提供select、insert、update、delete方法,用于完成和数据库的交互。

 

第五步:Executor执行器

MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。

Execute执行器起到至关重要的作用,它是真正执行Java与数据库交互的东西,参与了整个SQL查询执行过程中。

主要有三种执行器:

  • 简易执行器SIMPLE(不配置就是默认执行器);
  • REUSE是一种重用预处理语句;
  • BATCH批量更新批量专用处理器
public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}

 

第六步:MappedStatement对象

在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。

在Executor中doQuery()方法返回了封装的结果集,如下所示:

@Override
 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
   Statement stmt = null;
   try {
     Configuration configuration = ms.getConfiguration();
     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
     stmt = prepareStatement(handler, ms.getStatementLog());
     return handler.<E>query(stmt, resultHandler);
   } finally {
     closeStatement(stmt);
   }
 }

 

第七步:输入参数映射

输入映射,是在映射文件中通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型。

假设现在有个比较复杂的查询需求:完成用户信息的综合查询,需要传入查询条件很复杂。

可能包括:用户信息、其它信息,比如:商品、订单等,那么我们单纯的传入一个User就不行了,所以首先我们得根据查询条件,自定义一个新的pojo,在这个pojo中包含所有的查询条件。

package com.MyBatis.entity;

public class UserQueryVo {
    
    //在这里添加所需要的查询条件
    //用户查询条件,这里假设一个User就已经够了
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }   

    //可以包装其他的查询条件,比如订单、商品等
    //......    

}

 

第八步:输出结果映射

使用resultType进行输出映射,输出结果类型可以是Map、List等集合类型,只有查询出来的列名和POJO中的属性名一致,该列才可以映射成功。

拆解,后续我将重点分析其核心源码,这样先全局再局部,这样更有利于掌握其核心原理实现,希望这个框架系列能对你有所用。

 

Mybatis的缓存

MyBatis缓存机制详解(非常详细)

缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存可以避免频繁与数据库进行交互,从而提高查询响应速度。

MyBatis 缓存分为:一级缓存和二级缓存,如下图所示:

Mybatis框架教程(史上最全图文详解)-mikechen

1.MyBatis一级缓存

Mybatis一级缓存(作用原理与配置详解)

MyBatis一级缓存是MyBatis提供的一种缓存机制,即在同一会话中MyBatis会自动将第一次查询的结果集缓存,后续的同一查询不再调用数据库查询,大大提升了效率。

MyBatis一级缓存是session级别的缓存,生命周期是一次会话,session关闭一级缓存就失效了。

MyBatis在Executor中创建了本地缓存(一级缓存),如下图所示:

Mybatis框架教程(史上最全图文详解)-mikechen

大致的流程如下:

第一次查询用户id信息,先去缓存中查询是否有,如果没有,从数据库中查询用户信息,得到用户信息后在将用户信息储存到一级缓存中。

如果sqlSession去执行commit操作(插入、更新、删除),清空sqlSession中的一级缓存,保证缓存中始终保存的是最新的信息,避免脏读。

第二次查询用户id信息,先去缓存中查询,如缓存中有,直接从缓存中获取。

注意:两次查询须在同一个sqlsession中完成,否则将不会走MyBatis的一级缓存。

 

2.MyBatis二级缓存

Mybatis二级缓存详解(原理配置及应用场景)

二级缓存是mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的,二级缓存需要我们手动开启。

开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。

Mybatis框架教程(史上最全图文详解)-mikechen

二级缓存开启后,同一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多个 SqlSession 共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

MyBatis 是默认关闭二级缓存的,因为对于增删改操作频繁的话,那么二级缓存形同虚设,每次都会被清空缓存。

 

Mybatis配置文件

MyBatis配置文件详解(8大必备配置文件)

MyBatis配置文件主要分为两大类:MyBatis全局配置文件与MyBatis的mapper文件。

1.MyBatis全局配置

MyBatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。

MyBatis全局配置文件,格式如下:

<properties></properties><!-- 属性 -->
<settings></settings>    <!-- 设置-->
<typeAliases></typeAliases><!-- 配置别名 -->
<typeHandlers></typeHandlers><!-- 类型处理器 -->
<objectFactory></objectFactory><!-- 对象工厂 -->
<plugins></plugins><!-- 插件 -->
<environments default=""><!-- 环境配置-->
   <environment id=""><!-- 环境变量 -->
      <transactionManager></transactionManager><!-- 事务管理器 -->
      <dataSource></dataSource><!-- 数据源 -->
   </environment>
</environments>
<databaseIdProvider></databaseIdProvider><!-- 数据库厂商标识 -->
<mappers></mappers><!-- 映射器 -->

 

MyBatis全局配置示例如下:

<?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>
    <properties resource="database.properties"/>
    <environments default="MySqlDatabase" >
        <environment id="MySqlDatabase" >
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/hxstrive/mybatis/dynamic_sql/demo5/UserMapper.xml" />
    </mappers>
</configuration>

 

2.Mappers配置

这个就是用来配置sql映射语句的xml文件了,mappers文件共有4种配置方式。

1、直接配置xml映射文件全限定名

<mappers>
        <mapper resource="com/mikechen/mybatis/mapping/UserMapper.xml"/>
    </mappers>

2、通过url配置本地或者远程映射文件路径

<mappers>
        <mapper url="file://xxx.xxx.UserMapper.xml"></mapper>
    </mappers>
123

3、通过配置Mapper接口的方式来配置

<mappers>
        <mapper class="com.mikechen.mybatis.mapper.UserMapper"></mapper>
    </mappers>

4、通过配置Mapper接口包名的形式来配置

<mappers>
        <package name="com.mikechen.mybatis.mapper"/>
    </mappers>

注意:在使用第3和第4两种方式配置的时候,需要把xml映射文件和Mapper接口文件放在同一个目录,而且必须同名

 

Mybatis的分页

MyBatis分页详解(3种主流分页方式)

MyBatis分页主要有:原生Limit分页、RowBounds分页、以及PageHelper插件等MyBatis分页方式。

原生SQL的Limit分页

1.Limit语法

SELECT * FROM table LIMIT stratIndex,pageSize

 

2.修改Mapper文件

<select id="selectUser" parameterType="map" resultType="user">
  select * from user limit #{startIndex},#{pageSize}
</select>

 

3.业务代码

在实际使用limit,是根据前端传进来页码和每页的条数,在sql使用limit进行查询。

//分页查询 , 两个参数startIndex , pageSize
@Test
public void testSelectUser() {
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   int currentPage = 1;  //第几页
   int pageSize = 2;  //每页显示几个
   Map<String,Integer> map = new HashMap<String,Integer>();
   map.put("startIndex",(currentPage-1)*pageSize);
   map.put("pageSize",pageSize);

   List<User> users = mapper.selectUser(map);

   for (User user: users){
       System.out.println(user);
  }

   session.close();
}

 

使用PageHelper插件分页

PageHelper还是很好用的,主要分为如下三大步骤。

第一步:导入分页插件依赖

<!-- pagehelper 分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

 

第二步:在mybaits-config.xml配置使用分页插件

<plugins>
  <!-- 配置mybatis分页插件PageHelper -->
  <!-- com.github.pagehelper为PageHelper类所在包名 -->
  <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

 

第三步:业务层代码

@Test
    public void selectUserPageHelper() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        //第二种,Mapper接口方式的调用,推荐这种使用方式。
        PageHelper.startPage(1, 3);
        List<User> list = mapper.getUserInfo();
        //用PageInfo将包装起来
        PageInfo page = new PageInfo(list);
        for (User map: list){
            System.out.println(map);
        }
        System.out.println("page:---"+page);
        session.close();
    }

 

Mybatis使用场景

MyBatis适合数据量大(千万级),高并发的场景,表关联复杂度比较大,SQL需要优化的场景,项目要求对于数据库可控性好, 可深度调优。

作者简介

陈睿|mikechen,10年+大厂架构经验,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注作者「mikechen」公众号,获取更多技术干货!

评论交流
    说说你的看法