Mybatis
概述
什么是Mybatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
Mybatis能干什么
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
MyBatis 框架也被称之为 ORM(Object/Relational Mapping,即对象关系映射)框架。所谓的 ORM 就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中。
Mybatis - CURD使用
- 引入Maven依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
- 编写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核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/> // mysql 驱动
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8"/> // 数据库链接
<property name="username" value="root"/> // 账号
<property name="password" value="123456"/> // 密码
</dataSource>
</environment>
</environments>
</configuration>
- 编写mybatis工具类
//通过sqlSessionFactory工厂 生成sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
try {
//读取配置文件获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 从sqlSessionFactory中生成SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
- 编写实体类、mapper接口、mapper配置文件、注册mapper配置文件
实体类
public class User {
private int id;
private String name;
private String pwd;
// 构造方法、get、set 省略
}
mapper接口
public interface UserMapper {
// 编写操作数据库的接口方法
List<User> getUserList();
User getUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
mapper配置文件
<?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=绑定一个对应的Mapper接口-->
<mapper namespace="com.cjd.mapper.UserMapper">
<!--编写增删改查配置-->
<insert id="addUser" parameterType="com.cjd.pojo.User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
<update id="updateUser" parameterType="com.cjd.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id = #{id} ;
</update>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
<!--select查询语句-->
<select id="getUserList" resultType="com.cjd.pojo.User">
select * from mybatis.user
</select>
<select id="getUserById" resultType="com.cjd.pojo.User" parameterType="int">
select * from mybatis.user where id = #{id}
</select>
</mapper>
在mybatis核心配置文件中注册mapper配置文件
<mappers>
<mapper resource="com/cjd/mapper/UserMapper.xml"></mapper>
</mappers>
测试
@org.junit.Test
public void test01(){
SqlSession sqlSession = MybatisConfig.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
User userById = mapper.getUserById(1);
System.out.println(userById + "根据id查询");
for (User user : userList) {
System.out.println(user + " 查询全部");
}
int jdmagi = mapper.addUser(new User(4, "jdmagi", "123456"));
System.out.println(jdmagi+ "增加");
int zzs = mapper.updateUser(new User(2, "zzs", "222222"));
System.out.println(zzs+"修改");
int i = mapper.deleteUser(1);
System.out.println(i+"删除");
sqlSession.commit(); // 进行删除、修改、新增 需要进行事务的提交才能成功
sqlSession.close();
}
结果
User{id=2, name='zzs', pwd='222222'} 查询全部
User{id=3, name='ls', pwd='123456'} 查询全部
User{id=4, name='jdmagi', pwd='123456'} 查询全部
使用Map查询
编写mapper接口
//万能的Map
int addUser(Map<String,Object> map);
编写mapper配置
<!--对象中的属性,可以直接取出来 传递map的key-->
<insert id="addUser" parameterType="map">
insert into mybatis.user (id, pwd) values (#{userid},#{passWord});
</insert>
使用
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("passWord","2222333");
mapper.addUser2(map);
sqlSession.close();
}
Map传递参数,直接在sql中取出key即可! #{map中的key}
Mybatis - 配置详解
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
具体使用可以参考官网
https://mybatis.org/mybatis-3/zh/configuration.html#properties
常用配置:其它的一般都在整合的spring配置文件中
<?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>
<!-- 全局配置参数,需要时再设置 -->
<settings>
<!-- 打开延迟加载 的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 引入分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
Mybatis - resultMap
概念
什么是resultMap
结果集映射:即将数据库的属性与实体类的属性进行映射,默认是开启自动映射的。
resultMap有什么用
Mybatis默认开启自动结果集映射, 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap
能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
即,处理数据库属性与实体类属性名字不同
resultType
<!--resultType映射了实体类的全类名,会在幕后自动创建resultMap映射 是数据库属性与实体类属性一一对应-->
<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
resultMap
<!--结果集映射-->
<resultMap id="UserMap" type="User">
<!--column数据库中的字段,property实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap">
select * from mybatis.user where id = #{id}
</select>
复杂查询
环境搭建
-
新建表
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`, `name`) VALUES (1, '陈老师'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
-
创建实体类
@Data @AllArgsConstructor @NoArgsConstructor public class Student { private int id; private String name; private Teacher teacher; }
@Data @NoArgsConstructor @AllArgsConstructor public class Teacher { private int id; private String name; }
-
新建mapper接口
public interface StudentMapper { }
public interface TeacherMapper { }
-
新建mapper配置文件
<?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=绑定一个对应的Dao/Mapper接口--> <mapper namespace="com.cjd.mapper.类名"> </mapper>
-
在核心配置文件中注册
<mappers> <mapper resource="com/cjd/mapper/TeacherMapper.xml"></mapper> <mapper resource="com/cjd/mapper/StudentMapper.xml"></mapper> </mappers>
关联查询(查询实体类中的某个属性为其它实体类)
即将Student表的Teacher属性的值查询出来
- 编写mapper接口
public interface StudentMapper {
List<Student> getStudent();
}
方式一:通过查询嵌套查询
<?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=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.cjd.mapper.StudentMapper">
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="com.cjd.pojo.Student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<!--关联教师表的属性 select属性进行嵌套查询Teacher信息-->
<association property="teacher" column="tid" javaType="com.cjd.pojo.Teacher" select="getTeacher"></association>
</resultMap>
<select id="getTeacher" resultType="com.cjd.pojo.Teacher">
select * from teacher where id = #{id};
</select>
</mapper>
方式二:通过结果嵌套查询
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.cjd.mapper.StudentMapper">
<!--按照结果嵌套处理-->
<select id="getStudent" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid = t.id;
</select>
<!--即通过sql的查询语句查询出结果,然后对结果与实体进行映射-->
<resultMap id="StudentTeacher2" type="com.cjd.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.cjd.pojo.Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
查询集合
即将实体类属性为集合,集合是一个以上的实体类的聚集
如下:
@Data
public class Teacher {
private int id;
private String name;
//一个老师拥有多个学生
private List<Student> students;
}
方式一:按查询嵌套查询
<?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=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.cjd.mapper.StudentMapper">
<select id="getTeacher" resultMap="TeacherStudent">
select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<!--ofType指定集合的泛型类型,javaType指定实体类的属性类型-->
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<!--嵌套查询学生信息-->
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where tid = #{tid}
</select>
</mapper>
方式二:按结果嵌套查询
<?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=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.cjd.mapper.StudentMapper">
<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname, t.name tname,t.id tid
from student s,teacher t
where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
</mapper>
总结
- 关联 - association
- 集合 - collection
- javaType & ofType
- JavaType 用来指定实体类中属性的类型
- ofType 用来指定映射到List或者集合中的 pojo类型,泛型中的约束类型!
Mybatis - 日志、分页
日志
日志可以对我们进行开发调试、排错带来很大的帮助,mybatis的日志实现,默认是关闭,所以想要使用mybatis的日志时,需要去mybatis核心配置文件进行配置
主要有以下几种
-
SLF4J
-
LOG4J 【常用】
-
LOG4J2
-
JDK_LOGGING
-
COMMONS_LOGGING
-
STDOUT_LOGGING 【常用】
-
NO_LOGGING
配置STDOUT_LOGGING
<!--在核心配置文件中配置即可-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
配置LOG4J
引入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
编写LOG4J的配置文件log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
在mybatis核心配置文件中配置
<settings>
<setting name="logImpl" value=""/>
</settings>
分页
分页的作用:减少数据的处理量
方法一:使用SQL语句进行分页
mapper接口方法
// 分页接口方法
List<User> getUserByLimit(Map<String,Integer> map);
mapper配置
<!--//分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
方法二: RowBounds
sql查询的是全部数据,在调用方法的时候才实现分页
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//创建RowBounds
RowBounds rowBounds = new RowBounds(1, 2);
//通过Java代码层面实现分页
List<User> userList = sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
方法三: 使用分页插件(常用)
…
Mybatis - 注解开发
-
注解在mapper接口上实现
@Select("select * from user") List<User> getUsers(); // 方法存在多个参数,所有的参数前面必须加上 @Param("id")注解 @Select("select * from user where id = #{id}") User getUserByID(@Param("id") int id); @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})") int addUser(User user); @Update("update user set name=#{name},pwd=#{password} where id = #{id}") int updateUser(User user); @Delete("delete from user where id = #{uid}") int deleteUser(@Param("uid") int id);
-
在核心配置文件中绑定接口
<!--绑定接口--> <mappers> <mapper class="com.kuang.dao.UserMapper"/> </mappers>
-
正常使用即可
Mybatis - 动态SQL
什么是动态SQL
动态SQL就是根据不同的条件生成不同的SQL语句, 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
if
判断参数是否存在再进行sql语句条件的构造
mapper接口
public interface BlogMapper {
List<Blog> getBlogList(Map<String,String> map);
}
mapper配置
<select id="getBlogList" parameterType="map" resultType="com.cjd.pojo.Blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title},
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
测试
public void test01(){
SqlSession sqlSession = MybatisConfig.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("title","Java");
stringStringHashMap.put("author","cjd");
List<Blog> list = mapper.getBlogList(stringStringHashMap);
for (Blog blog : list) {
System.out.println(blog);
}
sqlSession.close();
}
结果
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@776aec5c]
==> Preparing: select * from blog where 1=1 and title = ? and author = ?
==> Parameters: Java(String), cjd(String)
<== Columns: id, title, author, create_time, views
<== Row: 1, Java, cjd, 2021-05-01 17:04:05.0, 111
<== Total: 1
Blog(id=1, title=Java, author=cjd, createTime=null, views=111)
choose
从多个条件中选择一个使用
mapper配置
<select id="getBlogList" parameterType="map" resultType="com.cjd.pojo.Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title},
</when>
<when test="author != null">
author = #{author},
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
测试
public void test01(){
SqlSession sqlSession = MybatisConfig.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("title","Java");
stringStringHashMap.put("author","cjd");
stringStringHashMap.put("views","222");
List<Blog> list = mapper.getBlogList(stringStringHashMap);
for (Blog blog : list) {
System.out.println(blog);
}
sqlSession.close();
}
结果
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3c46e67a]
==> Preparing: select * from blog WHERE title = ?
==> Parameters: Java(String)
<== Columns: id, title, author, create_time, views
<== Row: 1, Java, cjd, 2021-05-01 17:04:05.0, 111
<== Total: 1
Blog(id=1, title=Java, author=cjd, createTime=null, views=111)
trim、where、set
trim
<!--set 前后有逗号时都去除-->
<trim prefix="SET" suffixOverrides=",">
...
</trim>
where
<!--
where 只会在子元素生效的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 也会将它们去除。
-->
select * fromblog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
set
<!--
set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
-->
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
foreach
<!--
select * from mybatis.blog where 1=1 and (id=1 or id = 2 or id=3)
传递一个集合ids 在sql语句中遍历,以“and(”开始 “)”关闭 以“or”分割
-->
<select id="getBlogList" parameterType="map" resultType="com.cjd.pojo.Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id"
open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
SQL片段抽取
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="getBlogList" parameterType="map" resultType="com.cjd.pojo.Blog">
select * from blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
Mybatis - 缓存
缓存是一般的ORM框架都会提供的功能,目的是为了提升查询的效率和减少数据库的压力
Mybatis作为一个ORM框架也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口
一级缓存(本地缓存)
一级缓存也叫本地缓存,MyBatis 的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置。
同一次SqlSession测试
public void test01(){
SqlSession sqlSession = MybatisConfig.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
System.out.println("第一次查询");
mapper.getTeacher(1);
System.out.println("查询完成");
// 可以通过缓存清理进行再次从数据库中查询
// sqlSession.clearCache();
System.out.println("第二次查询");
mapper.getTeacher(1);
System.out.println("查询完成");
sqlSession.close();
}
第一次查询
[com.mysql.jdbc.JDBC4Connection@34a3d150]
==> Preparing: select * from mybatis.teacher where id = ?
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, 陈老师
====> Preparing: select * from mybatis.student where tid = ?
====> Parameters: 1(Integer)
<==== Columns: id, name, tid
<==== Row: 1, 小明, 1
<==== Row: 2, 小红, 1
<==== Row: 3, 小张, 1
<==== Row: 4, 小李, 1
<==== Row: 5, 小王, 1
<==== Total: 5
<== Total: 1
查询完成
第二次查询
查询完成
结果:第二次进行相同的查询是通过缓存获取的
缓存失效的情况
-
查询不同的东西
-
增删改操作
-
查询不同的Mapper.xml
-
手动清理缓存!
二级缓存
二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库。
使用
-
开启全局缓存
<!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/>
-
在要使用二级缓存的mappr配置文件中开启
<!--在当前Mapper.xml中使用二级缓存--> <cache/> ################################## 自定义参数 <!--在当前Mapper.xml中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
测试
public void test01(){
SqlSession sqlSession = MybatisConfig.getSqlSession();
SqlSession sqlSession2 = MybatisConfig.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
TeacherMapper mapper2 = sqlSession2.getMapper(TeacherMapper.class);
System.out.println("第一次查询");
mapper.getTeacher(1);
System.out.println("查询完成");
sqlSession.close();
System.out.println("第二次查询");
mapper2.getTeacher(1);
System.out.println("询完成");
sqlSession2.close();
}
结果
第一次查询
Cache Hit Ratio [com.cjd.mapper.TeacherMapper]: 0.0
[com.mysql.jdbc.JDBC4Connection@76505305]
==> Preparing: select * from mybatis.teacher where id = ?
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, 陈老师
Cache Hit Ratio [com.cjd.mapper.TeacherMapper]: 0.0
====> Preparing: select * from mybatis.student where tid = ?
====> Parameters: 1(Integer)
<==== Columns: id, name, tid
<==== Row: 1, 小明, 1
<==== Row: 2, 小红, 1
<==== Row: 3, 小张, 1
<==== Row: 4, 小李, 1
<==== Row: 5, 小王, 1
<==== Total: 5
<== Total: 1
查询完成
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@76505305]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@76505305]
Returned connection 1984975621 to pool.
第二次查询
As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
Cache Hit Ratio [com.cjd.mapper.TeacherMapper]: 0.3333333333333333
询完成
自定义缓存-ehcache
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
- 添加依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
- 在mapper配置文件指定
<!--在当前Mapper.xml中使用二级缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
- 编写ehcache.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--
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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>