目录
[#{}和{}区别](#{}和{}区别)
概述
MyBatis是一款优秀的半自动的ORM持久层框架,它支持自定义SQL,存储过程以及高级映射
MyBatis支持几乎所有的JDBC代码以及设置参数和获取结果集的工作
MyBatis将基本的JDBC常用接口封装,对外提供操作即可。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis是一款优秀的持久层框架。
那么什么是持久层呢?
持久层:可以立即保存在磁盘上
什么是ORM?
ORM(Object Relation Mapping),对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应的关系。例如,Java中的一个Student类,来对应数据库中的一张Student表,类中的属性和表中的列一一对应。Student类就对应Student表,一个Student对象就对应student表中的一行数据。
MyBatis的环境搭建
第一步.创建一张表和表对应的实体类
这里我创建了一个Student类和student表来举例
java
package com.ffyc.mybatis.model;
import java.io.Serializable;
public class Student implements Serializable {
private Integer id;
private Integer num;//建议不使用基本类型 建议使用包装类型 默认值是null
private String name;
private String gender;
private Major major;//类与类之间的关联关系 has-a 什么有什么
public Major getMajor() {
return major;
}
public void setMajor(Major major) {
this.major = major;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", num=" + num +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", major=" + major +
'}';
}
}
![](https://img-blog.csdnimg.cn/direct/cace7507481842d880f5e33037ad37d5.png)
第二步.导入MyBatis jar包和mysql数据驱动包
java
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
java
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
这里建议大家可以将这些常用的jar包整理起来,方便以后得使用。
第三步.创建MyBatis全局配置文件
需要将其放在一个名为mybatis.xml的文件中
java
<?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"><!--dtd文件是用来制定xml标签规范的-->
<configuration>
<environments default="development">
<environment id="development">
<!--
事务管理方式:就是一次对数据库操作过程中,执行多条sql管理
把所有的操作都成功执行后,再提交事务,让数据库最终执行本次提交的所有sql
-->
<!--事务管理方式-->
<transactionManager type="JDBC"/><!--使用jdbc事务管理-->
<!--数据库连接池配置
频繁地创建销毁与数据库的连接对象是比较占用时间和空间
可以在池子中默认创建若干个连接对象,有请求使用时,直接从连接池中取出一个对象,用完再还回去,
减少了创建和销毁的时间开销
-->
<dataSource type="POOLED">
<!--数据库连接-->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai" />
<property name="username" value="root" />
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
第四步.创建sql映射文件
sql映射文件放在resources目录下创建一个mapper目录 创建一个StudentMapper.xml文件
java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--dtd文件 叫xml的约束文件,定义xml标签-->
<mapper namespace="定义接口地址">
sql语句
</mapper>
在我们搭建好MyBatis环境后,我们需要测试一下。
定义一个接口StudentDao
java
package com.ffyc.mybatis.dao;
import com.ffyc.mybatis.model.Student;
import java.util.List;
public interface StudentDao {
List<Student> findStudents(Student student);
}
测试
我们可以查询一下我们在数据库中存储的学生信息
可以使用以下的代码来测试一下
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
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 javax.xml.ws.soap.Addressing;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class Test1 {
public static void main(String[] args) throws IOException {
//读入mybatis核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
//创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//为接口创建一个代理对象
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
//调用我们自己的方法
List<Student> studentList = studentDao.findStudents();
System.out.println(studentList);
sqlSession.commit();//提交数据库事务 事务只针对新增 修改 删除操作 查询不需要提交事务的
//关闭与数据库的会话对象
sqlSession.close();
}
}
读取MyBatis核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
创建SqlSession对象
SqlSession sqlSession =sqlSessionFactory.openSession();
为接口创建一个代理对象
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
API接口说明
SqlSessionFactory 接口
使用 SqlSessionFactory 来创建 SqlSession,一旦创建 SqlSessionFactory就会在整个应用过程中始终存在。由于创建开销较大,所以没有理由去销毁再创建它,一个应用运行中也不建议多次创建 SqlSessionFactory。
SqlSession 接口
Sqlsession 意味着创建与数据库链接会话,该接口中封装了对数据库操作的方法,与数据库会话完成后关闭会话。
由于SqlSessionFactory接口在整个过程中只会创建一次,所以为了简化我们在项目中的代码,我们可以把其创建过程放在一个静态代码块里。
具体的代码如下 我们可以创建一个MybatisUtil的Class文件
java
package com.ffyc.mybatis.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.Reader;
public class MybatisUtil {
static SqlSessionFactory sqlSessionFactory = null;
static {
//读入mybatis核心配置文件
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
/*
创建SqlSessionFactory对象
SqlSessionFactory对象是用来创建SqlSession对象的
由于SqlSessionFactory对象创建的开销较大,所以一个项目中只创建一个SqlSessionFactory对象,不用关闭
*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
在以后得测试代码中,我们就可以向以下的代码这样写
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class Test3 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
/*Student student = studentDao.findStudentById(1);
System.out.println(student);*/
/* List<Student> students = studentDao.findStudents();
System.out.println(students);*/
/* Student student = studentDao.findStudentById1(1);
System.out.println(student);*/
sqlSession.commit();
sqlSession.close();
}
}
这样的代码会更加简洁一些。
面向接口开发需要遵循的规范
1、 Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同。
2、 Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同。
3、 Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的
parameterType 的类型相同。
4、 Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的
resultType 的类型相同。
Mybatis日志
具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。
Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:
SLF4J|LOG4J|JDK_LOGGINGCOMMONS_LOGGING|STDOUT_LOGGING
配置方法
这个需要放在我们创建的StudentMapper文件中。
java
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/><!--打印执行日志-->
</settings>
参数传递
参数传递分为单个参数直接传递和多个参数使用@Param("id")绑定
单个参数直接传递
Admin findAdminById(int id);
多个参数使用@Param("id")绑定
Admin login(@Param("account") String account,@Param("password") String password);
具体代码如下
单个参数直接传递
java
<select id="findAdminById" parameterType="int" resultType="Admin">
select * from admin where id = #{id}
</select>
多个参数使用@Param("id")绑定
java
<select id="login" resultType="Admin">
select * from admin where account = #{account} and password = #{password}
</select>
如果传入一个复杂的对象,就需要使用paramterType参数进行类型定义:
例如:
void updateAdmin(Admin admin);
结果处理
对象映射
#{}和${}区别
#{}占位符,是经过预编译的,编译好SQL语句在取值,#方式能够防止sql注入
#{}:delete from admin where id = #{id}
结果:deletee from admin where id = ?
{}会将值以字符串形式拼接到sql语句,{}方式无法防止sql注入
{}:delete from admin where id = '{id}'
结果:delete from admin where id = '1'
一般是#{}向sql传值使用,而使用${}向sql传列名
例如在 order by ${column} 语句后面可以动态替换列名
特殊处理使用resultMap
定义resultMap
java
<resultMap id="adminmap" type="Admin">
<id property="id" column="id"></id><!--映射主键的-->
<result property="account" column="account"></result>
<result property="password" column="password"></result>
<result property="gender" column="gender"></result>
<result property="adminAge" column="admin_age"></result>
</resultMap>
使用resultMap
java
<select id="findAdminByAccount" parameterType="string" resultMap="adminmap">
select * from admin where account = #{account}
</select>
注意事项
1.resultMap的id属性是resultMap的唯一标识,本例中定义为"adminmap"
2.resultMap的type属性是映射的POJO类型
3.id标签映射主键,result标签映射非主键
4.property设置对象的属性名称,column映射查询结果的列名称
多表关联处理结果集
此处需要用到resultMap元素中association,collection元素
Collection元素处理一对多关联
这里我用学生和专业的关系来举例
专业与学生一对多关系
专业一方,配置多方集合
java
public class Major {
private Integer id;
private String name;
private List<Student> students;
}
学生多方,在多方配置一方
java
public class Student implements Serializable {
private Integer id;
private Integer num;//建议不使用基本类型 建议使用包装类型 默认值是null
private String name;
private String gender;
private Major major;//类与类之间的关联关系 has-a 什么有什么
}
使用resultMap组装查询结果
java
<!--自定义结果映射关系-->
<resultMap id="studentMap" type="Student"><!--最终返回一个学生对象 将学生信息封装学生对象-->
<id property="id" column="id"></id><!--映射主键的-->
<result property="num" column="num"></result>
<result property="name" column="name"></result>
<result property="gender" column="gender"></result>
<association property="major" javaType="Major">
<result property="name" column="mname"></result>
</association>
</resultMap>
<select id="findStudentById" parameterType="int" resultMap="studentMap">
SELECT
s.id,
s.num,
s.name,
s.gender,
m.name mname
FROM
student s LEFT JOIN major m ON s.majorid = m.id
WHERE
s.id = #{id}
</select>
嵌套查询
将一个多表关联查询拆分为多次查询 先查询主表数据,然后查询关联表数据。
java
<!--嵌套查询 把一个关联查询分成两个单表查询,然后通过学生专业外键,查询关联专业信息-->
<resultMap id="studentMap1" type="Student">
<association property="major" javaType="Major" select="findMajorById" column="majorid"></association>
</resultMap>
<select id="findStudentById1" parameterType="int" resultMap="studentMap1">
SELECT
s.id,
s.num,
s.name,
s.gender,
s.majorid
FROM
student s
WHERE
s.id = #{id}
</select>
<select id="findMajorById" parameterType="int" resultType="Major">
SELECT
*
FROM
major
WHERE
id = #{id}
</select>
注解方式
常用注解标签
@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select : 查询 sql, 和 xml select sql 语法完全一样
@Update : 更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param : 入参
@Results : 设置结果集合
@Result : 结果
使用案例
Mybatis动态SQL
可以根据具体的参数条件,来对SQL语句进行动态拼接。
if元素
if标签可以对传入的条件进行判断
java
<if test="name!=null">
name = #{name},
</if>
<if test="num!=null">
num = #{num},
</if>
<if test="gender!=null">
gender = #{gender}
</if>
where元素
where元素会进行判断,如果他包含的标签中有返回值的话,它就插入一个'where'
此外,如果标签返回的内容是以AND 或 OR开头,它会剔除掉AND 或 OR
java
<select id="findStudents" resultType="Student">
select * from student
<where>
<if test="name!=null">
name = #{name}
</if>
<if test="num!=0">
and num = #{num}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</where>
</select>
我们可以测试一下 看看运行时候的sql语句
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setName("张泽兴");
student.setGender("男");
student.setNum(20221808);
List<Student> students = studentDao.findStudents(student);
System.out.println(students);
sqlSession.commit();
sqlSession.close();
}
}
我们可以看到运行时的sql语句为
trim元素
让我们添加一个指定的前缀关键字,让我们去除指定的关键字
java
<!--trim 让我们添加一个指定的前缀关键字
让我们去除指定的关键字-->
<select id="findStudents" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<if test="name!=null">
name = #{name}
</if>
<if test="num!=0">
and num = #{num}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</trim>
</select>
测试代码
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setName("张泽兴");
student.setGender("男");
student.setNum(20221808);
List<Student> students = studentDao.findStudents(student);
System.out.println(students);
sqlSession.commit();
sqlSession.close();
}
}
sql语句
![](https://img-blog.csdnimg.cn/direct/2d6a76b7f0ae42d4a0bba9e5171c6dae.png)
choose元素
有时候我们不想使用所有的条件,而只是想从多个条件中选择一个使用。
choose元素有点像我们在JavaSE中学到的switch语句
java
<select id="findStudents" resultType="Student">
select * from student
<where>
<choose>
<when test="name!=null">
name = #{name}
</when>
<when test="num!=0">
and num = #{num}
</when>
<otherwise>
name = '李钰轩'
</otherwise>
</choose>
</where>
</select>
测试代码:
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setNum(20221807);
List<Student> students = studentDao.findStudents(student);
System.out.println(students);
sqlSession.commit();
sqlSession.close();
}
}
这里我们传入的student对象里有学生的学号,所以我们查询的结果就是为此学号的学生信息
![](https://img-blog.csdnimg.cn/direct/b90632a6165947a3b011bec39e513d57.png)
如果我们传入的student对象里的学生学号或者学生姓名为null 这时就会查询<otherwise></otherwise> 里设置的条件
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setNum(0);
student.setName(null);
List<Student> students = studentDao.findStudents(student);
System.out.println(students);
sqlSession.commit();
sqlSession.close();
}
}
以下为运行结果:
![](https://img-blog.csdnimg.cn/direct/899cea0e642b487298dd209f67760066.png)
set元素
set元素可以把最后一个逗号去掉
这个功能trim元素也可以实现
set元素实现代码
java
<!--
<set> 动态添加set关键字 还可以去掉最后的逗号
-->
<update id="updateStudent" parameterType="student">
update student
<set>
<if test="name!=null">
name = #{name},
</if>
<if test="num!=null">
num = #{num},
</if>
<if test="gender!=null">
gender = #{gender}
</if>
</set>
where id = #{id}
</update>
测试代码
java
<update id="updateStudent" parameterType="student">
update student
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name = #{name},
</if>
<if test="num!=null">
num = #{num},
</if>
<if test="gender!=null">
gender = #{gender}
</if>
</trim>
where id = #{id}
</update>
sql语言
![](https://img-blog.csdnimg.cn/direct/fdc143d6eb33432daaabef223b37798f.png)
trim元素实现代码
java
<update id="updateStudent" parameterType="student">
update student
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name = #{name},
</if>
<if test="num!=null">
num = #{num},
</if>
<if test="gender!=null">
gender = #{gender}
</if>
</trim>
where id = #{id}
</update>
测试代码
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setId(3);
student.setName("李四");
sqlSession.commit();
sqlSession.close();
}
}
sql语言
![](https://img-blog.csdnimg.cn/direct/3cd974d966824a42ac0bdefea22ec9b5.png)
foreach元素
主要用在构建 in 条件中,它可以在 SQL 语句中进行迭代一个集合。foreach
元素的属性主要有 item,index,collection,open,separator,close。
item 表示集合中每一个元素进行迭代时的别名,index 指定一个名字,用于
表示在迭代过程中,每次迭代到的位置,open 表示该语句以什么开始,
separator 表示在每次进行迭代之间以什么符号作为分隔符,close 表示以什
么结束,在使用 foreach 的时候最关键的也是最容易出错的就是 collection
属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的。
-- 如果传入的是单参数且参数类型是一个 List 的时候,collection 属 性值为 list
-- 如果传入的是单参数且参数类型是一个 array 数组的时候,collection 的属性值为 array
测试代码
java
<select id="findStudentsByColumn" resultType="Student">
select
<foreach collection="list" item="c" separator=",">
${c}
</foreach>
from student
</select>
特殊符号处理
在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:
特殊字符 转义字符
< <
> >
"
"
' '
&
&
除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如
下所示:
<if test="id != null">
AND <![CDATA[ id <> #{id} ]]>
</if>
<![CDATA[
]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略。
但是有个问题那就是 <if> </if> <where> </where>
<choose> </choose> <trim> </trim> 等这些标签都不会被解析,所以
我们只把有特殊字符的语句放在 <![CDATA[
]]> 尽量缩小<![CDATA[ ]]>
的范围
演示代码
java
<!--
xml 中特殊符号的处理
解决方式1:使用转义字符代替
解决方式2:<![CDATA[ 特殊符号]]>
-->
<select id="findStudents1" parameterType="int" resultType="Student">
select * from student where num <![CDATA[<]]> #{num}
</select>
MyBatis的一级缓存和二级缓存
为什么使用缓存
缓存的作用是为了减轻数据库的压力,提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库 执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。
Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。
二级缓存是多个 SqlSession 共享的,其作用域是同一个 namespace,不同的 sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率,Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存。
一级缓存
Mybatis 对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个 SqlSession 而言。所以在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用一个 Mapper 方法,往往只执行一次 SQL,因为使用 SelSession 第一次查询后MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
一级缓存工作模式
一级缓存演示代码
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
public class Test6 {
public static void main(String[] args) {
/*
mybatis 框架也是提供了缓存功能
一级缓存:是SqlSession级别的,在同一个Sqlsession中相同的两次查询,只查询一次数据库
将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可
一级缓存失效
1.执行增删改操作后,当前的一级缓存会清空
2.sqlSession.clearCache();强制清空一级缓存
3.sqlSession.close(); 关闭连接对象,清空一级缓存
*/
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudents1(1);//在同一个sqlSession中,再次查询相同的内容,会从一级缓存中查询,不会查询数据库
//执行删除操作,修改 新增操作后 会清空以及缓存数据
//sqlSession.clearCache();//强制清空一级缓存
studentDao.findStudents1(1);
sqlSession.commit();
sqlSession.close();
}
}
我们执行两次一样的查询操作,会发现在最后的输出结果中显示只会进行一次数据库的连接操作,因为以及缓存会将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可。
![](https://img-blog.csdnimg.cn/direct/45bb2a28f866437b9833eab91df18758.png)
但是如果我们在两次查询操作的中间加上
sqlSession.clearCache()(强制清空一级缓存),这时我们就会发现他会进行两次数据库的连接操作。
![](https://img-blog.csdnimg.cn/direct/e05a362cbd944dddaf751960ef9056fa.png)
二级缓存
二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用 mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。 每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读。
二级缓存配置操作
第一步.启用二级缓存
在我们前面创建的mybatis.xml文件中加入
java
<!--启用二级缓存-->
<setting name="cacheEnabled" value="true"/>
cacheEnabled 设置为 true 时启用二级缓存,设置为 false 时禁用二级缓存。
第二步.对象序列化
在我们创建时实体类对象实现序列化接口java.io.Serialzable
java
public class Student implements Serializable {
}
第三步.配置映射文件
在我们定义的StudentMapper.xml映射文件中添加<cache />
java
<!--设置二级缓存配置的
size="20" 缓存对象数量
flushInterval=""设置二级缓存的有效时间
-->
<cache size="20" flushInterval="3000"></cache>
二级缓存工作模式
二级缓存演示代码
java
package com.ffyc.mybatis.test;
import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
public class Test7 {
public static void main(String[] args) {
/*
mybatis 框架也是提供了缓存功能
一级缓存:是SqlSession级别的,在同一个Sqlsession中相同的两次查询,只查询一次数据库
将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可
一级缓存失效
1.执行增删改操作后,当前的一级缓存会清空
2.sqlSession.clearCache();强制清空一级缓存
3.sqlSession.close(); 关闭连接对象,清空一级缓存
二级缓存:
二次缓存是sqlSessionFactory级别(sqlSessionFactory对象只有一个,创建后就不关闭了,多个Sqlsession共享一个sqlSessionFactory)
使用是需要配置的
1.启用二级缓存
<setting name="cacheEnabled" value="true"/>
2.对象序列化
将所有的 POJO 类实现序列化接口 Java.io. Serializable
3.配置映射文件
在 Mapper 映射文件中添加<cache />,表示此mapper 开启二级缓存
*/
/*
SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
用完关闭即可
*/
SqlSession sqlSession = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudents1(2);
sqlSession.commit();
sqlSession.close();
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
//获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
studentDao1.findStudents1(2);
sqlSession1.commit();
sqlSession1.close();
}
}
因为二级缓存是SqlSessionFactory 级别的,SqlSessionFactory对象只有一个,这里我们创建两个sqlsession对象,用来进行同样的查询操作,会发现在结果中1只进行了一次数据库的连接操作。
![](https://img-blog.csdnimg.cn/direct/486ef3a254d1462190c3669e48710e83.png)