详解Mybatis,超详细的!!!

🌈hello,你好鸭,我是Ethan,一名不断学习的码农,很高兴你能来阅读。
✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。
🏃人生之义,在于追求,不在成败,勤通大道。加油呀!

🔥个人主页:Ethan Yankang
🔥专栏:mybatis||必知必会工具集
🔥本篇概览:mybatis基本使用、Mapper.xml映射文件、核心架构、一二级缓存、EhCache专业缓存使用


Mybatis

一、概述

1.mybatis是什么?有什么特点?

它是一款半自动的ORM持久层框架具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低

2.什么是ORM?

Object Relation Mapping,对象关系映射对象指的是Java对象关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系,比如用一个Java的Student类,去对应数据库中的一张student表,类中的属性和表中的列一一对应。Student类就对应student表,一个Student对象就对应student表中的一行数据

3.为什么mybatis是半自动的ORM框架?

用mybatis进行开发,需要手动编写SQL语句而全自动的ORM框架,如hibernate则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialect),所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多

4.为什么需要 MyBatis?

传统的弊病在这里插入图片描述

引出 MyBatis
在这里插入图片描述
在这里插入图片描述

5.MyBatis 工作示意图

在这里插入图片描述

二、快速入门

  1. 创建 mybatis 数据库 - monster 表
CREATE DATABASE `mybatis` CREATE TABLE `monster` (
`id` INT NOT NULL AUTO_INCREMENT, `age` INT NOT NULL, `birthday` DATE DEFAULT NULL, `email` VARCHAR(255) NOT NULL , `gender` TINYINT NOT NULL, `name` VARCHAR(255) NOT NULL, `salary` DOUBLE NOT NULL,
PRIMARY KEY (`id`)
) CHARSET=utf8
  1. 创建 maven 项目, 方便项目需要 jar 包管理
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    说明: 将 D:\idea_java_projects\HSP_MyBatis\pom.xml 作为父项目 pom.xml 引入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
  
<groupId>com.hspedu</groupId>
<artifactId>HSP_MyBatis</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<!-- 导入依赖 -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3. 创建 resources/mybatis-config.xml

<?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://127.0.0.1:3306/mybatis?useSSL=true&amp;
useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="hsp"/>
</dataSource>
</environment>
</environments>
</configuration>
  1. 创 建java\com\hspedu\entity\Monster.java
package com.hspedu.entity;
import java.util.Date;
/**
* @author 韩顺平|
* @version 1.0
*/
//老韩解读
//1. 一个普通的 Pojo 类
 
//2. 使用原生态的 sql 语句查询结果还是要封装成对象
//3. 要求大家这里的实体类属性名和表名字段保持一致。
public class Monster {
private Integer id;
private Integer age;
private String name;
private String email;
private Date birthday;
private double salary;
private Integer gender;
public Monster() {
}
public Monster(Integer id, Integer age, String name, String email, Date birthday, double
salary, Integer gender) {
this.id = id;
this.age = age;
this.name = name;
this.email = email;
this.birthday = birthday;
this.salary = salary;
 
this.gender = gender;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public double getSalary() {
 
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Monster{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", email='" + email + '\'' +
 
", birthday=" + birthday +
", salary=" + salary +
", gender=" + gender +
'}';
}
}
  1. 创 建java\com\hspedu\mapper\MonsterMapper.java
package com.hspedu.mapper;
import com.hspedu.entity.Monster;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author 韩顺平
* @version 1.0
*/
 
public interface MonsterMapper {
//添加方法
public void addMonster(Monster monster);
}
  1. 创 建java\com\hspedu\mapper\MonsterMapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper: 表 示 一 个 映 射 器 , 文
档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#insert_update_and_delete
1. namespace="com.hspedu.mapper.MonsterMapper" 说明本 mapper.xml 文件是用来 映射管理 MonsterMapper 接口
,主要是去实现 MonsterMapper 接口声明方法
2. select: 实现一个查询操作 insert:表示一个添加操作
3. id="addMonster" 表示 MonsterMapper 接口 的方法名
4. resultType="xx" 返回的结果类型,如果没有就不需要写
5. parameterType="com.hspedu.entity.Monster" 表示该方法输入的参数类型
6. (age,birthday,email,gender,name,salary) 表的字段名
7. #{age},#{birthday},#{email},#{gender},#{name},#{salary} 是实体类 Monster 的属性名
 
-->
<mapper namespace="com.hspedu.mapper.MonsterMapper">
<!--没有在 mybatis-config.xml 指定 typealiases 时,需要给 Monster 指定全类名
<insert id="addMonster" parameterType="com.hspedu.entity.Monster"-->
<!-- useGeneratedKeys="true" keyProperty="id"-->
<!-- >-->
<insert id="addMonster" parameterType="com.hspedu.entity.Monster" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO monster (age,birthday,email,gender,name,salary)
VALUES(#{age},#{birthday},#{email},#{gender},#{name},#{salary})
</insert>
</mapper>
  1. 修 改main\resources\mybatis-config.xml
</environments>
 
<mappers>
<!-- 这里会引入(注册)我们的 Mapper.xml 文件 -->
<mapper resource="com/hspedu/mapper/MonsterMapper.xml"/>
</mappers>
  1. 创 建 工 具 类java\com\hspedu\util\MyBatisUtils.java
package com.hspedu.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* @author 韩顺平
* @version 1.0
韩顺平 Java 工程师
*/
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用 mybatis 第一步:获取 sqlSessionFactory 对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 老韩解读
* 1. 获得 SqlSession 的实例
* 2. SqlSession 提供了对数据库执行 SQL 命令所需的所有方法。
* 3. 通过 SqlSession 实例来直接执行已映射的 SQL 语句
* @return
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
 
}
}
  1. 创建并测试src\test\java\com\hspedu\mapper\MonsterMapperTest.java
package com.hspedu.mapper;
import com.hspedu.entity.Monster;
import com.hspedu.util.MyBatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.*;
/**
* @author 韩顺平
韩顺平 Java 工程师
* @version 1.0
*/
public class MonsterMapperTest {
//这个是 Sql 会话,通过它可以发出 sql 语句
private SqlSession sqlSession;
private MonsterMapper monsterMapper;
@Before
public void init() throws Exception {
//通过 SqlSessionFactory 对象获取一个 SqlSession 会话
sqlSession = MyBatisUtils.getSqlSession();
//获取 MonsterMapper 接口对象, 该对象实现了 MonsterMapper
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
System.out.println(monsterMapper.getClass());
}
@Test
public void addMonster() {
for (int i = 0; i < 1; i++) {
Monster monster = new Monster();
monster.setAge(100 + i); monster.setBirthday(new Date());
 
monster.setEmail("tn@sohu.com");
monster.setGender(1); monster.setName("松鼠精" + i);
monster.setSalary(9234.89 + i * 10);
monsterMapper.addMonster(monster);
System.out.println("刚刚添加的对象的 id=" + monster.getId());
}
//增删改,需要提交事务
if (sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("保存成功!");
}
}
  1. 测试,看看是否可以添加成功, 这时会出现找不到 Xxxxmapper.xml 错误, 分析了原因
    在这里插入图片描述

  2. 解决找不到 Mapper.xml 配置文件问题, 老韩提示:如果顺利,你会很快解决,不顺利,
    你 会 干 到 怀 疑 人 生
    在这里插入图片描述

//在父工程的 pom.xml 加入 build 配置
</dependencies>
<!--在 build 中配置 resources,来防止我们资源导出失败的问题
老韩解读:
1. 不同的 idea/maven 可能提示的错误不一样
2. 不变应万变,少什么文件,就增加相应配置即可
-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
韩顺平 Java 工程师
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
12. 修改 MonsterMapper.java, 增加方法接口
//根据 id 删除一个 Monster

public void delMonster(Integer id);
  1. 修改 MonsterMapper.xml, 实现方法接口
<delete id="delMonster" parameterType="Integer">
DELETE FROM monster
WHERE id=#{id}
</delete>
  1. 完 成 测 试 , 修 改
    D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\test\java\com\hspedu\mapper\MonsterMapperTest.java, 增加测试方法
//测试 mybatis 删除功能
@Test
public void delMonster() {
monsterMapper.delMonster(2);
if (sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("删除 ok");
}
  1. 修改 MonsterMapper.java, 增加方法接口
    //修改 Monster
public void updateMonster(Monster monster);

//查询-根据 id

public Monster getMonsterById(Integer id);

//查询所有的 Monster

public List<Monster> findAllMonster();
  1. 修改 MonsterMapper.xml, 实现方法接口
  1. 为了配置方便,在 mybatis-config.xml 配置 Monster 的别名
<configuration>
<typeAliases>
<!-- 为某个 mapper 指定一个别名, 下面可以在 XxxxxMapper.xml 做相应简化处理
-->
<typeAlias type="com.hspedu.entity.Monster" alias="Monster"/>
</typeAliases>
  1. 修改 MonsterMapper.xml, 实现方法接口, 可以使用 Monster 别名了.
<update id="updateMonster" parameterType="Monster">
UPDATE monster SET age=#{age}, birthday=#{birthday}, email = #{email}, gender= #{gender}, name=#{name}, salary=#{salary} WHERE id=#{id}
</update>
<select id="getMonsterById" parameterType="Integer"
resultType="Monster">
SELECT * FROM monster WHERE id = #{id}
</select>
<!-- 实现 findAllMonster -->
<select id="findAllMonster"
resultType="Monster">
SELECT * FROM monster
</select>
  1. 完成测试, 修改 MonsterMapperTest.java , 增加测试方法
    //测试 mybatis 的修改
@Test
public void updateMonster() {
Monster monster = new Monster();
monster.setAge(200);
monster.setBirthday(new Date());
monster.setEmail("hspedu@sohu.com");
monster.setGender(2);
monster.setName("狐狸精");
monster.setSalary(9234.89);
 
monster.setId(2);
monsterMapper.updateMonster(monster);
if (sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("修改 ok");
}
//测查询单个对象
@Test
public void getMonsterById() {
Monster monster = monsterMapper.getMonsterById(2);
System.out.println(monster);
if (sqlSession != null) {
sqlSession.close();
}
}
@Test
public void findAllMonster() {
List<Monster> monsterList = monsterMapper.findAllMonster();
 
for (Monster monster : monsterList) {
System.out.println(monster);
}
if (sqlSession != null) {
sqlSession.close();
}
}

2.3 日志输出-查看 SQL

  1. 在开发 MyBatis 程序时,比如执行测试方法,程序员往往需要查看 程序底层发给 MySQL的 SQL 语句, 到底长什么样, 怎么办?
    修 改
    D:\java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\resources\mybatis-config.x
    ml, 加入日志输出配置, 方便分析 SQL 语句
<configuration>
<!-- 配置 MyBatis 自带的日志输出, 还可以是其它日志比如 SLF4J | LOG4J | LOG4J2 |
JDK_LOGGING 等 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

在这里插入图片描述

三、注解开发

1.MyBatis-原生的 API 调用
原生的 API 快速入门需求
● 在前面项目的基础上,将增删改查,使用 MyBatis 原生的 API 完成,就是直接通过
SqlSession 接口的方法来完成
如下——

具体案例请见上面

2.MyBatis-注解的方式操作
注解的 方式快速入门需求
● 在前面项目的基础上,将增删改查,使用 MyBatis 的注解的方式完成
1.增加
2.删除
3.修改
4.查询

(1). 创 建MonsterAnnotation.java
D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\java\com\hspedu\mapper\MonsterAnnotation.java

package com.hspedu.mapper;
import com.hspedu.entity.Monster;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @author 韩顺平
* @version 1.0
*/
public interface MonsterAnnotation {
//添加方法,将我们的 sql 语句直接写在@Insert 注解即可
@Insert("INSERT INTO monster (age,birthday,email,gender,name,salary) " + "VALUES(#{age},#{birthday},#{email},#{gender},#{name},#{salary})")
public void addMonster(Monster monster);
 
//根据 id 删除一个 Monster
@Delete("DELETE FROM monster " + "WHERE id=#{id}")
public void delMonster(Integer id);
//修改 Monster
@Update("UPDATE monster SET age=#{age}, birthday=#{birthday}, " + "email = #{email},gender= #{gender}, " + "name=#{name}, salary=#{salary} " + "WHERE id=#{id}")
public void updateMonster(Monster monster);
//查询-根据 id
@Select("SELECT * FROM monster WHERE " + "id = #{id}")
public Monster getMonsterById(Integer id);
//查询所有的 Monster
@Select("SELECT * FROM monster ")
public List<Monster> findAllMonster();
}

(2). 修改 mybatis-config.xml , 对 MonsterAnnotaion 进行注册

<mappers>
<!-- 这里会引入(注册)我们的 Mapper.xml 文件 -->
<mapper resource="com/hspedu/mapper/MonsterMapper.xml"/>
<!--老韩解读
1. 如果是通过注解的方式,可不再使用 MonsterMapper.xml
2. 但是需要在 mybatis-config.xml 注册含注解的类
-->
<mapper class="com.hspedu.mapper.MonsterAnnotation"/>

(3). 创 建src\test\java\com\hspedu\mapper\MonsterAnnotationTest.java , 完成测试

package com.hspedu.mapper;
import com.hspedu.entity.Monster;
import com.hspedu.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
/**
* @author 韩顺平
* @version 1.0
*/
public class MonsterAnnotationTest {
//这个是 Sql 会话,通过它可以发出 sql 语句
private SqlSession sqlSession;
@Before
public void init() throws Exception {
//通过 SqlSessionFactory 对象获取一个 SqlSession 会话
sqlSession = MyBatisUtils.getSqlSession();
}
//测试通过注解的方式来完成接口中方法的实现
//其它的删除,修改,查询是一样一样,同学们自己测试即可
@Test
public void addMonster() {
Monster monster = new Monster();
monster.setAge(500);
monster.setBirthday(new Date());
monster.setEmail("hspedu@sohu.com");
monster.setGender(2);
monster.setName("白虎精");
monster.setSalary(9234.89);
MonsterAnnotation monsterAnnotation =
sqlSession.getMapper(MonsterAnnotation.class);
monsterAnnotation.addMonster(monster);
//增删改,需要提交事务
if (sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("操作成功");
}
}

3.注意事项和说明

  1. 如 果 是 通 过 注 解 的 方 式 , 就 不 再 使 用 MonsterMapper.xml 文 件 , 但 是 需 要 在
    mybatis-config.xml 文件中注册含注解的类/接口
  2. 使用注解方式,添加时, 如果要返回自增长 id 值, 可以使用**@Option** 注解 , 组合使用
@Insert(value = "INSERT INTO `monster` (`age`, `birthday`, `email`, `gender`, `name`, `salary`) " +
"VALUES (#{age}, #{birthday}, #{email}, #{gender}, #{name}, #{salary})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
public void addMonster(Monster monster);

四、mybatis-config.xml-配置文件详解

mybatis 的核心配置文件(mybatis-config.xml),比如配置 jdbc 连接信息,注册 mapper
等等,我们需要对这个配置文件有详细的了解
在这里插入图片描述
1.properties 属性
通过该属性,可以指定一个外部的 jdbc.properties 文件,引入我们的 jdbc 连接信息

  1. 创 建D:\idea_java_projects\HSP_MyBatis\01_mybatis_quickstart\src\main\resources\jdbc.properties
jdbc.user=root
jdbc.password=hsp
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?userSSL=true&amp;userUnicode=true&amp;char
acterEncoding=UTF-8
jdbc.driver=com.mysql.jdbc.Driver
  1. 修改 mybatis-confing.xml
<configuration>
<!-- 这里就是引入 jdbc.properties 文件 -->
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!-- <property name="url"
韩顺平 Java 工程师
value="jdbc:mysql://127.0.0.1:3306/mybatis?userSSL=true&amp;-->
<!-- userUnicode=true&amp;characterEncoding=UTF-8"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="hsp"/>-->
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
  1. 修改父项目的 pom.xml(如果已经配置了*.properties 就不用再配置) 并完成测试
    在这里插入图片描述

2.settings 全局参数定义
settings 列表,通常使用默认

3.typeAliases 别名处理器
1.别名是为 Java 类型命名一个短名字。它只和 XML 配置有关,用来减少类名重复的部分
2.如果指定了别名,我们的 MappperXxxx.xml 文件就可以做相应的简化处理
3.注意指定别名后,还是可以使用全名的
举例说明
修 改01_mybatis_quickstart\src\main\resources\mybatis-config.xml

<typeAliases>
<!-- 如果一个包下有很多的类,我们可以直接引入包,这样
该包下面的所有类名,可以直接使用
-->
<package name="com.hspedu.entity"/>
<!-- 为某个 mapper 指定一个别名, 下面可以在 XxxxxMapper.xml 做相应简化处理 -->
<!-- <typeAlias type="com.hspedu.entity.Monster" alias="Monster"/> -->
</typeAliases>
2) 修改 MonsterMapper.xml 并完成测试
<!--没有在 mybatis-config.xml 指定 typealiases 时,需要给 Monster 指定全类名
<insert id="addMonster" parameterType="com.hspedu.entity.Monster"-->
<!-- useGeneratedKeys="true" keyProperty="id"-->
<!-- >-->
<insert id="addMonster" parameterType="Monster" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO monster (age,birthday,email,gender,name,salary)
VALUES(#{age},#{birthday},#{email},#{gender},#{name},#{salary})
</insert>

4.typeHandlers 类型处理器
在这里插入图片描述
5.environments 环境

  1. resource 注册 Mapper 文件: XXXMapper.xml 文件(常用,使用过)
    在这里插入图片描述

2 class:接口注解实现(使用过)

在这里插入图片描述

3.package 方式注册 :

<package name="com.hspedu.mapper"/> 并测试
<!-- 老韩解读
1. 当一个包下有很多的 Mapper.xml 文件和基于注解实现的接口时,
为了方便,我们可以以包方式进行注册
2. 将下面的所有 xml 文件和注解接口 都进行注册
-->
<package name="com.hspedu.mapper"/>
</mappers>

五、XxxxMapper.xml-SQL 映射文件

1、MyBatis 的真正强大在于它的语句映射(在 XxxMapper.xml 配置), 由于它的异常强大, 如
果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。
MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

2、SQL 映射文件常用的几个顶级元素(按照应被定义的顺序列出):

cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素
parameterType - 将会传入这条语句的参数的类全限定名或别名。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。

3.XxxMapper.xml-详细说明
新建 Module xml-mapper

1、在原来的项目中,新建 xml-mapper 项目演示 xml 映射器的使用
2、新建 Module 后,先创建需要的包,再将需要的文件/资源拷贝过来(这里我们拷贝
Monster.java、resources/jdbc.properties 和 mybatis-config.xml)
3、创建 MonsterMapper.java MonsterMapper.xml 和 MonsterMapperTest.java , 做一个比
较干净的讲解环境, 可以再写一遍, 让学员加深印象.
在这里插入图片描述
基本使用

  1. insert、delete、update、select 这个我们在前面讲解过,分别对应增删改查的方法和 SQL
    语句的映射
    .
  2. 如何获取到刚刚添加的 Monster 对象的 id 主键 [前面使用过了]
    在这里插入图片描述

3.parameterType(输入参数类型)

● parameterType(输入参数类型)

  1. 传入简单类型,比如按照 id 查 Monster(前讲过)
  2. 传入 POJO 类型,查询时需要有多个筛选条件
  3. 当有多个条件时,传入的参数就是 Pojo 类型的 Java 对象,比如这里的 Monster 对象
  4. 当传入的参数类是 String 时,也可以使用 ${} 来接收参数

parameterType-应用案例

  • 案例 1:请查询 id = 1 或者 name = ‘白骨精’ 的妖怪

  • 案例 2:请查询 name 中 包含 “牛魔王” 的妖怪

1 修改 MonsterMapper.java, 增加方法接口

//通过 id 或者名字查询
public List<Monster> findMonsterByNameORId(Monster monster);
//查询名字中含义'精'妖怪
public List<Monster> findMonsterByName(String name);
  1. 修改 MonsterMapper.xml
<!-- 实现 findMonsterByNameORId -->
<select id="findMonsterByNameORId" parameterType="Monster"
resultType="Monster">
SELECT * FROM monster
WHERE id=#{id} OR name=#{name}
</select>
<!-- 看看模糊查询的使用 取值 需要 ${value} 取值-->
<select id="findMonsterByName" parameterType="String" resultType="Monster">
SELECT * FROM monster
WHERE name LIKE '%${value}%' </select>
  1. 修改 MonsterMapperTest.java ,完成测试
//演示多条件的查询
@Test
public void findMonsterByNameORId() {
Monster monster = new Monster();
monster.setId(5);
monster.setName("白虎精");
List<Monster> list = monsterMapper.findMonsterByNameORId(monster);
for (Monster monster2 : list) {
System.out.println(monster2);
}
if (sqlSession != null) {
sqlSession.close();
}
}
//测试一个模糊查询
@Test
public void findMonsterByName() {
List<Monster> list = monsterMapper.findMonsterByName("精");
for (Monster monster : list) {
System.out.println(monster);
}
 
if (sqlSession != null) {
sqlSession.close();
}
}

六、核心架构

在这里插入图片描述

七、缓存

1.一级缓存

  1. 默认情况下,mybatis 启用的是一级缓存/本地缓存/local Cache,它是 SqlSession 级别的。
  2. 同一个 SqlSession 接口对象调用了相同的 select 语句,会直接从缓存里面获取,而不是再去查询数据库
    一级缓存原理图
    在这里插入图片描述
    需求: 当我们第 1 次查询 id=1 的 Monster 后,再次查询 id=1 的 monster 对象,就会直接从一级缓存获取,不会再次发出 sql

2.一级缓存失效分析

  1. 关闭 sqlSession 会话后, 再次查询,会到数据库查询, 修改 MonsterMapperTest.java, 测
    试一级缓存失效情况

  2. 当执行 sqlSession.clearCache() 会使一级缓存失效,修改 MonsterMapperTest.java, 测
    试一级缓存失效情况

  3. 当对同一个 monster 修改,该对象在一级缓存会失效, 修改 MonsterMapperTest.java, 测
    试一把

3.二级缓存

  1. 二级缓存和一级缓存都是为了提高检索效率的技术
  2. 最大的区别就是作用域的范围不一样,一级缓存的作用域是 sqlSession 会话级别,在一次
    会话有效,而二级缓存作用域是全局范围,针对不同的会话都有效
    .
    ● 二级缓存原理图
    在这里插入图片描述

4.二级缓存快速入门

快速入门

(1). mybatis-config.xml 配置中开启二级缓存

<configuration>
<!-- 这里就是引入 jdbc.properties 文件 -->
<properties resource="jdbc.properties"/>
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>

(2) 使用二级缓存时 entity 类实现序列化接口 (serializable),因为二级缓存可能使用到序列化技术
在这里插入图片描述
(3)在对应的 XxxMapper.xml 中设置二级缓存的策略

<mapper namespace="com.hspedu.mapper.MonsterMapper">
<!-- 老韩解读
1. 针对这个 monster 对象的二级缓存策略和相关设置
eviction="FIFO" : 缓存策略 先进先出, 细节会再说
flushInterval="30000": 每 30000 毫秒 刷新一次,和数据库保持一致. size: 二级缓存设置最大保持的 360 个对象, 超过了,就启用 fifo 策略处理 ,默认
1024
readOnly: 只读,为了提高效率
-->
<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>

(4)修改 MonsterMapperTest.java , 完成测试

@Test
public void level2CacheTest() {
Monster monster = monsterMapper.getMonsterById(2);
System.out.println("--" + monster + "--");
//----------------------------测试 2 级缓存-----------------------

//1. 当我们关闭 sqlSession 会话后,再次查询 id =2 的 monster 时,如果有二级缓存
// 就会从二级缓存读取. 不会发出 sql
System.out.println("=======关闭 sqlSession 会话后, 再次查询同一数据,如果有二级
缓存,不会到数据库查询========");
sqlSession.close();
sqlSession = MyBatisUtils.getSqlSession();
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
monster = monsterMapper.getMonsterById(2);
System.out.println("--" + monster + "--");
if (sqlSession != null) {
sqlSession.commit();
sqlSession.close();
}
System.out.println("操作成功");
}

5.二级缓存注意事项和使用陷阱

(1)理解二级缓存策略的参数

<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>

上面的配置意思如下:

创建了 FIFO 的策略,每隔 30 秒刷新一次,最多存放 360 个对象而且返回的对象被认为是 只读的。
eviction:缓存的回收策略
flushInterval:时间间隔,单位是毫秒
size:引用数目,内存大就多配置点,要记住你缓存的对象数目和你运行环境的可用内存资源数目。 默认值是 1024。
readOnly:true,只读

(2)四大策略

LRU最近最少使用的:移除最长时间不被使用的对象,它是默认
√ FIFO –先进先出:按对象进入缓存的顺序来移除它们。
√ SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
√ WEAK –弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

6.如何禁用二级缓存

1.修改mybatis-config.xml

<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 默认就是 true-->
<setting name="cacheEnabled" value="false"/>
</settings>

2.修改mapper\MonsterMapper.xml

<!--<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>-->

3.或者更加细粒度的, 在配置方法上指定
在这里插入图片描述

设置 useCache=false 可以禁用当前 select 语句的二级缓存,即每次查询都会发出 sql 去查询,默认情况是 true,即该 sql 使用二级缓存。
注意:一般我们不需要去修改,使用默认的即可

  1. mybatis 刷新二级缓存的设置
<update id="updateMonster" parameterType="Monster" flushCache="true">
UPDATE mybatis_monster SET NAME=#{name},age=#{age} WHERE id=#{id}
</update>

insert、update、delete 操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读,默认为 true,默认情况下为 true 即刷新缓存,一般不用修改。

Mybatis 的一级缓存和二级缓存执行顺序
一句话:缓存执行顺序是:二级缓存–>一级缓存–>数据库

7.一二级缓存关系

  1. 不会出现一级缓存和二级缓存中有同一个数据。因为二级缓存(数据)是在一级缓存关闭之后才有的

2.在一级缓存存在的情况下,依然是先查询二级缓存,但是因为二级缓存,没有数据, 所以命中率都是 0.0

8.EhCache 缓存

  1. EhCache 是一个纯 Java 的缓存框架,具有快速、精干等特点
  2. MyBatis 有自己默认的二级缓存(前面我们已经使用过了),但是在实际项目中,往往使用的是更加专业的第三方缓存产品 作为 MyBatis 的二级缓存,EhCache 就是非常优秀的缓存产品

在这里插入图片描述
(1)配置和使用 EhCache

  1. 加入相关依赖, 修改 D:\idea_java_projects\HSP_MyBatis\03_mybatis_cache\pom.xml
<dependencies>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>

(2)mybatis-config.xml 仍然打开二级缓存

  <settings><!-- 开启二级缓存,默认就是打开 --><setting name="cacheEnabled" value="true"/></settings>

(3) 加 入src\main\resources\ehcache.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--diskStore:为缓存路径,ehcache 分为内存和磁盘两级,此属性定义磁盘的缓存位置。
参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--defaultCache:默认缓存策略,当 ehcache 找不到定义的缓存时,则使用这个缓存策略。
只能定义一个。
-->
<!--name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout 将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当 eternal=false
对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时
间和失效时间之间。仅当 eternal=false 对象不是永久有效时使用,默认是 0.,也就是对象存活
时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between
restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置 DiskStore(磁盘缓存)的缓存区大小。默认是
30MB。每个 Cache 都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是 120 秒。
memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根
据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为 FIFO(先进先出)
或是 LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先
进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来
最少被使用的。如上面所讲,缓存的元素有一个 hit 属性,hit 值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量
满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的
元素将被清出缓存。
-->
<defaultCache  eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/>
</ehcache>

(4) 在 XxxMapper.xml 中启用 EhCache , 当然原来 MyBatis 自带的缓存配置就注销了

<mapper namespace="com.hspedu.mapper.MonsterMapper">
<!-- 老韩解读
1. 针对这个 monster 对象的二级缓存策略和相关设置
eviction="FIFO" : 缓存策略 先进先出, 细节会再说
flushInterval="30000": 每 30000 毫秒 刷新一次,和数据库保持一致. size: 二级缓存设置最大保持的 360 个对象, 超过了,就启用 fifo 策略处理 ,默认
1024
readOnly: 只读,为了提高效率
-->
<!-- <cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>-->
<!-- 启动 ehcache 缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

(5)在 XxxMapper.xml 中启用 EhCache , 当然原来 MyBatis 自带的缓存配置就注销了

<mapper namespace="com.hspedu.mapper.MonsterMapper">
<!-- 老韩解读
1. 针对这个 monster 对象的二级缓存策略和相关设置
eviction="FIFO" : 缓存策略 先进先出, 细节会再说
flushInterval="30000": 每 30000 毫秒 刷新一次,和数据库保持一致. size: 二级缓存设置最大保持的 360 个对象, 超过了,就启用 fifo 策略处理 ,默认
1024
readOnly: 只读,为了提高效率
-->
<!-- <cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>-->
<!-- 启动 ehcache 缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

(6)修改 MonsterMapperTest.java

//测试 ehCache 级缓存,及其失效情况
@Test
public void ehCacheTest() {
Monster monster = monsterMapper.getMonsterById(2);
System.out.println("--" + monster + "--");
//----------------------------测试 ehCache 缓存-----------------------
//. 当我们关闭 sqlSession 会话后,再次查询 id =2 的 monster 时,如果有 ehCache 缓// 就会从 ehCache 缓存读取. 不会发出 sql
System.out.println("=======关闭 sqlSession 会话后, 再次查询同一数据,如果有
ehCache 缓存,不会到数据库查询========");
sqlSession.close();
sqlSession = MyBatisUtils.getSqlSession();
monsterMapper = sqlSession.getMapper(MonsterMapper.class);
monster = monsterMapper.getMonsterById(2);
 
System.out.println("--" + monster + "--");
if (sqlSession != null) {
sqlSession.close();
}
System.out.println("操作成功");
}

9.EhCache 缓存-细节说明

如何理解 EhCache 和 MyBatis 缓存的关系
  1. MyBatis 提供了一个接口 Cache【如右图,找到 org.apache.ibatis.cache.Cache ,关联源码包就可以看到 Cache 接口】

  2. 只要实现了该 Cache 接口,就可以作为二级缓存产品和 MyBatis 整合使用,Ehcache 就是实现了该接口

  3. MyBatis 默认情况(即一级缓存)是使用的 PerpetualCache 类实现 Cache 接口的,是核心类
    在这里插入图片描述
    在这里插入图片描述

  4. 当我们使用了 Ehcahce 后,就是 EhcacheCache 类实现 Cache 接口的,是核心类
    在这里插入图片描述

  5. 我们看一下源码,发现缓存的本质就是 Map<Object,Object>
    在这里插入图片描述

💖💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖💖💖💖💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​​💖​​​​​​

热门专栏推荐

​​​​​​

🌈🌈计算机科学入门系列 关注走一波💕💕
🌈🌈CSAPP深入理解计算机原理 关注走一波💕💕
🌈🌈微服务项目之黑马头条 关注走一波💕💕
🌈🌈redis深度项目之黑马点评 关注走一波💕💕
🌈🌈Java面试八股文系列专栏 关注走一波💕💕
🌈🌈算法leetcode+剑指offer 关注走一波💕💕


📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤ 分享👥 留言💬thanks!!!
📚愿大家都能学有所得,功不唐捐!

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值