概述
原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了 Google Code,随着开发团队转投Google Code 旗下, iBatis3.x正式更名为MyBatis
MyBatis 是一款优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码手动设置参数以及手动获取结果集的操作
MyBatis 可以使用 XML 或注解来配置和映射,将数据库中的记录映射成Java 的 POJO(Plain Old Java Objects,普通的 Java 对象),是一种 ORM(ORM Object Relational Mapping 对象关系映射)实现. 它支持动态 SQL 以及数据缓存. Mybatis 将基本的 JDBC 常用接口封装,对外提供操作即可
Mybatis 中文官网 mybatis – MyBatis 3 | 入门
MyBatis 环境搭建
1.创建一张表和表对应的实体类
2.导入 MyBatis jar 包,mysql 数据库驱动包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
3.创建 MyBatis 全局配置文件
在resources文件夹内创建mybaitsConfig.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>
<!-- mybaits全局配置文件 -->
<!-- 配置连接数据库信息 -->
<environments default="development">
<environment id="development">
<!-- 事务管理使用jdbc默认的 事务是一次对数据操作过程的管理,必须是满足原子性特质 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED"> <!-- 是否使用数据库连接池功能 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="101917"/>
</dataSource>
</environment>
</environments>
<!-- 注册sql映射-->
<mappers>
<mapper resource="mappers/AdminMapper.xml"/>
</mappers>
</configuration>
也可以创建comfig.properties来放属性,再引入
driveName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai
username = root
password = 101917
引入
<!-- 读入属性文件 -->
<properties resource="config.properties"></properties>
<!-- 配置连接数据库信息 -->
<environments default="development">
<environment id="development">
<!-- 事务管理使用jdbc默认的 事务是一次对数据操作过程的管理,必须是满足原子性特质 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED"> <!-- 是否使用数据库连接池功能 -->
<property name="driver" value="${driveName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
4.创建 sql 映射文件
<?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">
<!--namespace="com.mysql.admin" 命名空间必须唯一-->
<!--<mapper namespace="com.mysql.admin">-->
<mapper namespace="mybaitsdemo.dao.AdminDao">
<insert id="saveAdmin" parameterType="mybaitsdemo.model.Admin">
/*sql语句*/
insert into admin(account,password,gender)
values(#{account},#{password},#{gender})
</insert>
</mapper>
可以在配置文件中为类定义别名
<!-- 配置类型别名 -->
<typeAliases>
<!-- <typeAlias type="mybaitsdemo.model.Admin" alias="Admin"></typeAlias> -->
<package name="mybaitsdemo.model"/>
</typeAliases>
<insert id="saveAdmin" parameterType="Admin"></insert>
<--参数别名-->
5.定义接口
public interface AdminDao {
/*
方法名与对应的mapper中的id一致
方法参数,返回值类型 与mapper标签中定义的参数类型,返回值一致
*/
int saveAdmin(Admin admin);//保存学生到数据库,返回的值是操作行数(int型)
}
6.测试 MyBatis
读取核心配置文件
创建sqlSessionFactory
创建SqlSession
获得接口的代理对象
代理对象.接口中的方法
//读取mybatis核心配置文件 配置的地址
Reader reader = Resources.getResourceAsReader("mybaitsConfig.xml");
//创建SQLSessionFactory对象
//由于SqlSessionFactory对象创建开销较大,所以一旦创建就不用销毁,一个应用程序只有一个由于SqlSessionFactory对象创建开销较大
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建SQLSession对象,表示一次与数据库连接对话,类似于JDBC中的connection
//每次与数据库交互一次,都会创建一个SqlSession对象,使用完后,就立即销毁释放
SqlSession sqlSession = sessionFactory.openSession();
//获得映射接口的代理对象
AdminMapper adminMapper = sqlSession.getMapper(AdminMapper.class);
//使用代理对象调用接口中的方法,本质上并不是调用接口中的方法
//实际调用的是与接口中方法名相同的xml文件中对应的某一个sql
adminMapper.saveAdmin(admin);
//提交事物,最终告诉数据库我所有的执行没有问题,在数据库中执行
sqlSession.commit();//手动提交事务
sqlSession.close();//关闭与数据库连接
进行简单的封装,建立util
package mybaitsdemo.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;
public class mybatisUtil {
static SqlSessionFactory sqlSessionFactory =null;
//当mybatisUtil类第一次加载时,静态代码块执行,创建SqlSessionFactory
static{
//读取mybaits全局配置文件
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybaitsConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
//创建sqlSessionFactory,由sqlSessionFactory创建sqlSession
//由于sqlSessionFactory创建开销较大,用于封装数据库连接信息,所以没有必要每次创建新的
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
//获得SqlSession
public static SqlSession getsqlSession(){
return sqlSessionFactory.openSession();
}
}
测试
package mybaitsdemo.test;
import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
import mybaitsdemo.dao.AdminDao;
import mybaitsdemo.model.Admin;
import mybaitsdemo.util.mybatisUtil;
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;
public class text1 {
public static void main(String[] args) throws IOException {
//创建sqlSession 使用此对象建立与数据库会话 类似等同于Connection,保证每一个线程都是私有的不共享,用完关闭
SqlSession sqlSession = mybatisUtil.getsqlSession();
//(命名空间,参数)
//Admin admin = sqlSession.selectOne("mybaitsdemo.dao.AdminDao.findAdminById",1);
//动态为接口生成一个代理对象(mybatis框架生成),由代理对象去调用mapper文件中的方法
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = adminDao.findAdminById(1);//由代理对象访问sql
System.out.println(admin);
sqlSession.close();
}
}
Mybatis-Dao 层 Mapper 接口化开发
Mapper 接口开发方式只需要程序员编写 Mapper 接口,由 Mybatis 框架创建接口的动态代理对象,使用 sqlsession.getMapper(接口.class);获得代理对象. Mapper 接口开发需要遵循以下规范:
-
Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同.
-
Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同.
-
Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
-
Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同
Mybatis 日志
具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。Mybatis 内置的日志工厂提供日志功能,在mybaitsConfig.xml文件中配置:
settings放在properties属性文件下面
<!-- 读入属性文件 -->
<properties resource="config.properties"></properties>
<settings>
<!-- 日志功能 -->
<setting name="logImpl" value="STDOUT_LOGGING"/
</settings>
添加单元测试
添加Junit依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--Test 也是不会被打包到war包-->
<scope>provided</scope>
</dependency>
进行单元测试
@Test
public void save() {
SqlSession sqlSession = mybatisUtil.getsqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
Admin admin = new Admin("keke", "222", "女");
int res = adminDao.saveAdmin(admin);
System.out.println(res);
sqlSession.commit();//提交数据库事务,告诉mysql所有的逻辑执行完了,你可以将所有的sql执行
sqlSession.close();
}
参数传递
单个参数直接传递
Admin findAdminById(int id);
<select id="findAdminById" parameterType="int" resultType="Admin">
select id,account,password,gender xb,admin_phone from admin where id = #{id}
</select>
多个参数使用@Param(“ ”)绑定
Admin login(@Param("account") String account,@Param("password") String password);
<select id="login" resultType="Admin">
select * from admin where account=#{account} and password=#{password}
</select>
如果传入一个复杂的对象,就需要使用 parameterType 参数进行类型定义,例如:
int saveAdmin(Admin admin);
<insert id="saveAdmin" parameterType="Admin">
insert into admin(account,password,gender)
values(#{account},#{password},#{gender})
</insert>
增删改查
<insert id="唯一标识" useGeneratedKeys="把新增加的主键赋值到自己定义的keyProperty " keyProperty="接收主键的属性" parameterType="参数类型">
insert into
user(userName,userAge)values(#{userName},#{userAge})
</insert>
#{} 和${}区别
#{} 占位符,是经过预编译的,编译好 SQL 语句再取值,#方式能够防止 sql 注入
#{}:select * from t_user where uid=#{uid}
${} 拼接符,会传入参数字符串,取值以后再去编译 SQL 语句,$方式无法防止 Sql注入 ${}
${}:select * from t_user where uid= '1'
注意:MyBatis 排序时使用 order by 动态参数时需要注意,用$而不是#
修改,删除,查询(单个参数)
接口
int updateAdmin(Admin admin);
int deleteAdmin(int id);
Admin findAdminById(int id);
AdminMapper.xml
<update id="updateAdmin" parameterType="Admin">
update admin set account=#{account},password=#{password},gender=#{gender}
where id = #{id}
</update>
<delete id="deleteAdmin" parameterType="int">
delete from admin where id = #{id}
</delete>
<!-- SQL语句
查询结果为一条记录,可以实现将记录数据自动封装到对象中
有前提条件:1.表中列名与对象属性名完全一致
2.开启mapUnderscoreToCamelCase 列名属性名的自动转换
-->
<select id="findAdminById" parameterType="int" resultType="Admin">
select id,account,password,gender xb,admin_phone from admin where id = #{id}
</select>
测试
如果添加驼峰命名自动映射,需要在配置文件中添加setting设置
<!-- 从经典的数据命名 a_column 转为Java的驼峰命名 aColumn -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
结果处理
查询所有数据
传入list集合中,配置文件中的代码与查询单条数据相同,mybatis会自动将查询到的数据包装成集合返回
List<Admin> findAdminList();
<select id="findAdminList" resultType="Admin">
select * from admin
</select>
查询数据条数
返回基本数据类型,直接接收
int findAdminCount();
<!--
resultType 返回值的类型定义
resultType="Admin" 自定义类类型 已经配置过别名 所以可以直接使用简称
-->
<select id="findAdminCount" resultType="int">
select count(*) from admin
</select>
POJO 对象输出映射
如果表中的类名与类中的属性名完全相同,mybatis会自动将查询结果封装到POJO对象中
如果java中使用标准驼峰命名,数据库中使用下划线连接命名,可以开始全局设置实现自动转换
<setting name="mapUnderscoreToCamelCase" value="true"/>
Admin findAdminById(int id);
<select id="findAdminById" parameterType="int" resultType="Admin">
select id,account,password,gender xb,admin_phone from admin where id = #{id}
</select>
特殊处理定义 resultMap
特殊情况下,列名与属性名就是不一致,resultMap解决映射问题
<resultMap id="studentMap" type="Student">
<!-- id标签 映射主键 -->
<id column="id" property="id"></id>
<!-- result标签 映射普通属性 -->
<result column="no" property="no"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<!-- association标签 映射关联对象 会创建关联对象,将数据封装到关联对象中 -->
<association property="grade" javaType="Grade">
<result column="gname" property="name"></result>
</association>
<association property="admin" javaType="Admin">
<result column="account" property="account"></result>
</association>
</resultMap>
<select id="findStudentById" resultMap="studentMap">
<include refid="selectStudent"></include>
WHERE s.id = #{id}
</select>
resutlMap 的 id 属性是 resutlMap 的唯一标识
resutlMap 的 id 属性是映射的 POJO 类
id 标签映射主键,result 标签映射非主键
property 设置 POJO 的属性名称,column 映射查询结果的列名称
多表关联处理结果集
association – 复杂类型联合; 许多查询结果合成这个类型
一对一结果映射–association 能引用自身, 或者从其它地方引用
collection – 复杂类型集合
嵌套结果映射– collection 能引用自身, 或者从其它地方引用
多对一与一对多
association 关联元素处理“有一个”类型的关系,即一对一关联。它有两种关联 方式
嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型
嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集
Collection 关联元素处理一对多关联
<resultMap id="gradeMap" type="Grade">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<collection property="students" javaType="list" ofType="Student">
<id column="sid" property="id"></id>
<result column="sname" property="name"></result>
</collection>
</resultMap>
<select id="findGradeList" resultMap="gradeMap">
SELECT
g.*,
s.id sid,
s.name sname
FROM
grade g
LEFT JOIN student s
ON g.id = s.gradeid
</select>
注解方式
一般在简单的SQL时使用
常用注解标签
@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select : 查询 sql, 和 xml select sql 语法完全一样
@Update : 更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param : 入参
@Results : 设置结果集合
@Result : 结果
使用案例
//插入数据
@Insert("insert into grade(name)value(#{name })")
int saveGrade(Grade grade);
//删除数据
@Delete("delete from grade where id = #{id}")
int deleteGrade(Grade grade);
动态 SQL
MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦
MyBatis 中用于实现动态 SQL 的元素主要有: If where trim set choose (when, otherwise) foreach
If
if 标签可以对传入的条件进行判断
<update id="updateStudent" parameterType="Student">
update * from student where id = 1
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="phone!=null">
phone=#{phone}
</if>
</update>
where
使用where标签,可以自动删除and或or等开头的关键字
<!--
if test="条件"
where 一旦where标签中的if有成立的条件,会动态的添加where关键字
一旦有像 and or 这样的关键字,可以动态删除
-->
<select id="findStudentList" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="no!=null & no!=''">
no=#{no}
</if>
<if test="name!=null & name!=''">
and name=#{name}
</if>
<if test="gender!=null & gender!=''">
and gender=#{gender}
</if>
</where>
</select>
trim
where 标签,其实用 trim 也可以表示,当 WHERE 后紧随 AND 或则 OR 的时候,就去除 AND 或者 OR。prefix 前缀,prefixOverrides 覆盖首部指定内容
<!--trim添加前缀
<trim prefix="where" prefixOverrides="and|or">
当if有成立的条件时,添加一个指定的前缀
prefixOverrides 以设定的条件开头的话可以覆盖掉
-->
<select id="findStudentList" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<if test="no!=null & no!=''">
no=#{no}
</if>
<if test="name!=null & name!=''">
and name=#{name}
</if>
<if test="gender!=null & gender!=''">
and gender=#{gender}
</if>
</trim>
</select>
<!--set也可以用trim代替-->
<update id="updateEmployee" parameterType="Employee">
update employee
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name = #{name},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="dept.id!=null">
deptId = #{dept.id},
</if>
</trim>
where id = #{id}
</update>
set
set标签可以动态添加set关键字
可以去掉set语句中最后一个逗号
<update id="updateStudent" parameterType="Student">
update student
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="phone!=null">
phone=#{phone}
</if>
</set>
where id = #{id}
</update>
choose
<!--
trim prefix="自定义的前缀" prefixOverrides="覆盖指定的关键字"
choose
when test = "条件" 成立执行when
otherwise 不成立执行otherwise
-->
<select id="findStudentList" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<choose>
<when test="name!=null">
name = #{name}
</when>
<otherwise>
name = "李四"
</otherwise>
</choose>
</trim>
</select>
foreach
主要用在构建 in 条件中,它可以在 SQL 语句中进行迭代一个集合
foreach元素的属性主要有 item,index,collection,open,separator,close
item 表示集合中每一个元素进行迭代时的别名
index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置
open 表示该语句以什么开始
separator 表示在每次进行迭代之间以什么符号作为分隔符
close 表示以什么结束
在使用 foreach 的时候最关键的也是最容易出错的就是 collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的
– 如果传入的是单参数且参数类型是一个 List 的时候,collection 属性值为 list
– 如果传入的是单参数且参数类型是一个 array 数组的时候,collection 的属性值为array
<!--集合-->
<select id="findStudentList1" resultType="mybaitsdemo.model.Student">
select * from student where no in
<foreach collection="list" item="no" open="(" separator="," close=")">
#{no}
</foreach>
</select>
<!--数组-->
<select id="findStudentList2" resultType="mybaitsdemo.model.Student">
select * from student where no in
<foreach collection="array" item="no" open="(" separator="," close=")">
#{no}
</foreach>
</select>
特殊符号处理
在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:
特殊字符 | 转义字符 |
---|---|
< | < |
> | > |
" | " |
' | ' |
& | & |
除了可以使用上述转义字符外,还可以使用< ![CDATA[ ]]>来包裹特殊字符
<select id="findStudentList3" resultType="mybaitsdemo.model.Student">
<!-- select * from student where no < 005 -->
select * from student where no <![CDATA[ < ]]> 005
</select>
缓存
缓存(cache)的作用是为了减轻数据库的压力,提高数据库的性能
原理
由于从硬盘上读数据相对来说比较慢,可以将第一次查询到内存中的数据放在缓存中
大量的查询时,数据库的压力会比较大,可以将第一次查询到的数据放在内存中不销毁
下一次查询的时候可以直接从内存中获取
这样就可以减轻数据库的压力
缓存是使用Map集合缓存数据的
一级缓存
一级缓存的作用域是一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,只会查询一次,第一次查询后将数据存在缓存,第二次直接从缓存中读取,从而提高查询效率
一级缓存生命周期
1、MyBatis 在开启一个数据库会话时,会 创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新的 PerpetualCache 对象,如果 SqlSession 调用了 close()方法,会释放掉一级缓存 PerpetualCache 对象,一级缓存将不可用。
2、如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用
3、SqlSession 中执行了任何一个 update 操作(update()、delete()、 insert()) ,都会清空 PerpetualCache 对象的数据,但是该对象可以继续使用
@Test
public void cacheDemo() {
SqlSession sqlSession = mybatisUtil.getsqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setId(2);
student.setName("李四");
List<Student> students = studentDao.findStudentList(student);
System.out.println(students);
//studentDao.updateStudent(student);//增删操作后,会清空一级缓存
//sqlSession.clearCache();清空一级缓存
System.out.println("---------------------------------------------------------");
student.setName("李四");
List<Student> students1 = studentDao.findStudentList(student);
System.out.println(students1);
sqlSession.close();//关闭sqlSession 会清空一级缓存
}
二级缓存
二 级 缓 存 是 多 个 SqlSession 共 享 的 , 其 作 用 域 是 mapper 的 同 一 个namespace,不同的 sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库 中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率
Mybatis 默认没有开启二级缓存需要在 setting 全局参 数中配置开启二级缓存
每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存
Mybatis 内部存储缓存使用一个 HashMap,key 为 hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。 sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读
配置
第一步:启用二级缓存
<!--开启全局的二级缓存配置-->
<setting name="cacheEnabled" value="true"/>
第二步:POJO 序列化
将所有的 POJO 类实现序列化接口 Java.io. Serializable
public class Student implements Serializable
第三步:配置映射文件
<!--表示此 mapper 开启二级缓存。
当 SqlSeesion 关闭时,会将数据存入到二级缓存.
eviction="FIFO" FIFO 缓存
flushInterval="60000" 缓存刷新时间,以毫秒为单位
size="512" 存储512个结果对象或列表的引用
readOnly="true" 返回的对象是只读的
可用的回收算法如下:
LRU – 最近最少使用:移出最近最长时间内都没有被使用的对象。(默认的)
FIFO –先进先出:移除最先进入缓存的对象。
SOFT – 软引用: 基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才进行回
收)。
WEAK – 弱引用: 基于垃圾回收机制和弱引用规则(垃圾回收器扫描到时即进行回
收)
-->
<cache flushInterval="1000"></cache>
使用二级缓存
@Test
public void cacheDemo1() {
SqlSession sqlSession = mybatisUtil.getsqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
List<Student> students = studentDao.findStudentList(student);
sqlSession.close();//关闭sqlSession 会清空一级缓存
SqlSession sqlSession1 = mybatisUtil.getsqlSession();
StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
List<Student> students1 = studentDao1.findStudentList(student);
sqlSession.close();//关闭sqlSession 会清空一级缓存
}
MyBatis 架构![](https://i-blog.csdnimg.cn/blog_migrate/b7f57fcdf4a5aeba63b4c971d0f10255.png)