除了mybatis,其他框架都是用注解更方便。由于mybatis中需要写sql语句的特殊性,mybatis是个例外,用xml配置文件更方便
一、配置文件详解
1.1 核心配置文件
官方建议命名为mybatis-config.xml,核心配置文件里可以进行如下的配置:
<environments> 和 <environment>
mybatis可以配置多套环境(开发一套、测试一套、、、),
在<environment>里必须要配置<transactionManager>和<dataSource>两个标签,<transactionManager>有两种选择JDBC|MANAGED,一般就写JDBC就行。
<properties>标签引入外部 xx.properties文件,我们把各种配置信息写在properties文件里,然后在<property>标签用${}符号引入。实现了其他配置信息和mybatis配置信息的解耦。
新建一个db.properties文件,
这样,如果数据库的信息需要更改,我们只需要更改db.properties文件就可以了,和mybatis毫无关系。
当然,我们也可以在<properties>标签里配置属性,但是不推荐这样。当同时在db.properties文件和<properties>标签里配置了相同的属性时,优先使用.properties文件里的。如下图所示,虽然我们在<properties>标签里配置了driver的属性,但是生效的依然是db.properties文件里的配置。
<typeAliases>起别名:之前我们在Mapper.xml里指定resultType和parameterType时,一直用的全限定名,有多个SQL语句就要重写多次全限定名,别名的作用就在于简化全限定名,意义仅在于让我们少打几个字,看着舒服些。有两种起别名的方式:
方式(一):直接起别名
方式(二):指定包名,这个包下的所有类都可以用自己类名的小写(推荐小写,也可以用大写)作为别名了。和方式(一)相比的缺点在于不能自己diy一个新的名字,也不是不可以,可以用@Alias注解实现。。。
<settings>标签
<settiings>标签极为重要,因为会改变mybatis的运行时行为。下面框出来的两个是最常用的设置。除此之外,还有日志工厂的设置,下面会单独拿出来讲。
<plugins> 一些强有力的插件
<mappers>映射器标签,有4种定义方式,以后一律用第1种。
但是还是讲一下方式(三)和方式(四)必须满足的两点:
- UserMapper.xml文件和UserMapper接口必须同名
- UserMapper.xml文件和UserMapper接口必须在同一个包下
二、生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致严重的并发问题。
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactory,就不再需要它了
- 只使用一次,所以它是局部变量
SqlSessionFactory:
- 可以想象为数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没问任何理由丢弃它或重新创建另一个实例。
- 因此SqlSessionFactory的最佳作用域是应用作用域
- 最简单就是使用单例模式或者静态单例模式
SqlSession:
- 想象为连接到连接池的一个请求
- SqlSession的实例不是线程安全的,因此是不能被共享的,最好的实践是把它放在一个方法里
- 用完之后需要赶紧关闭 sqlSession.close() ,否则会占据资源
三、ResultMap结果集 是mybatis最重要的属性
结果集标签<resultMap>的作用是什么?当实体类的属性名字和数据库中的字段名字不匹配时,用<resultMap>标签将二者映射/对应起来。
比如,当我们将实体类User的密码属性从“passWord”改为“pwdpwd”,而数据库中的字段仍然是password时,密码会被mysql查出来,但是映射不到程序当中去,所以程序产出的结果是null。
有两个很简单的办法,一个是更改代码,一个是更改mysql查询语句,但是都不推荐,因为我们不希望代码发生变动。先来看一下
① 更改代码:只需要把setter和getter方法的名字改成数据库字段的名字。mybatis的类型匹配器就自动给匹配上了。对了,之前没提到过类型匹配器。它的作用就是在如下代码中把mysql的查询结果根据命名,依靠setter方法,set到实体类上去。
List<User> allUser = userMapper.getAllUser(); //类型匹配器负责把mysql的结果匹配到实体类上
② 修改SQL语句,给数据库字段起别名,别名就是实体类中跟字段名不一样的那个的属性名
这两种方法都修改了原来已经写好的代码,并不建议这样做!
真正实践中的做法是用<resultMap>标签实现数据库字段名和实体类属性名的映射。
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserMapper">
<!--随便起一个id, 被下面的select标签的resultMap属性引用-->
<resultMap id="UserMap" type="pojo.User">
<result column="password" property="pwdpwd"/> <!--column是数据库的字段,property是实体类的属性-->
</resultMap>
<!--注意是resultMap不是resultType-->
<select id="getAllUser" resultMap="UserMap" >
select * from `user`
</select>
</mapper>
但是,世界总是这么简单就好了。上面我们只是查了user这一个数据库表,但是很多时候需要连表查询,那么resultMap标签的type属性就不能是任何一个属性了,因为是多个表的字段组成的结果,这时候这么办呢?
四、日志工厂
在mybatis核心配置文件mybatis-config.xml里配用<settings><setting>标签置日志工厂。
作用:输出完整的SQL语句和执行过程,帮助我们排错
<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
</settings>
</configuration>
logImpl 可选的值有:
- SLF4J
- LOG4J (常用)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (常用,标准日志)
- NO_LOGGING
- 或者其他
logImpl不带有默认值,所以当我们没有手动设置logImpl时,默认不输出任何日志。
以标准日志为例演示:
4.1 Log4j 讲解
1、先导包
使用一个外部类,先导包,一个开源包,被很多公司使用
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
19年教程里正常是导log4j包,但是我看他说以后在log4j-core更新,那我就下一个log4j-core试一试
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
并且我看jd也是这样用的
不行,log4j-core要配合slf4j这个统一接口才能使用,单独用会出错,单独用的话还是下载2012年停止更新(因为太完善了)的log4j 1.2.17。
PS: 解释什么是slf4j
① 当我们使用Log4j这个日志库处理日志信息时,就与其特定的类库发生了耦合,而日志库还有java.util.logging、logback等等,想象一下,若你的项目使用的是Log4j,当哪天你的项目需要引入别人编写的一个组件,而这个组件却是使用logback处理日志的,那我们就不得不导入两个实现同样功能的jar包并且维护两套日志配置了。
这时候就体现出Slf4j统一规范接口的价值了,因为Slf4j只是一个接口,我们只是通过调用slf4j接口的日志方法统一打印日志,可以忽略日志的具体实现方法。② 而log4j-core就是配合slf4j这个接口而写的,所以它叫“core”,外面再去套slf4j这个接口。
2、配置文件
Log4j支持两种配置文件格式,一种是XML格式的文件,一种是properties(key=value)文件,其中properties格式的配置文件最为常用,其有一个固定的文件名log4j.properties。
PS: 貌似log4j2开始,xml更常用了,因为jd用的就是log4j2.xml。在springboot里我们也讲过xml比properties减少了冗余。
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.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
3、使用
因为不止一个方法里用到logger去打印日志,所以我们要给它提升作用域,作为类的static final变量。
static final Logger logger = Logger.getLogger(MyTest.class);
五、分页查询
法(一)用limit
法(二)RowBounds——不会SQL语句也能实现的分页
老方法了,没有mysql效率快,现在没人用了
六、使用注解开发
学了spring之后我们知道注解可以代替xml配置文件,mybatis里自然也可以。
我们把UserMapper.xml删掉,核心配置文件里自然也就不需要绑定Mapper.xml配置文件,而是直接绑定UserMapper接口,在接口方法上用@Select注释写sql语句。
但是,我们很容易想到,@Select注释只适合于简单的sql语句(确实会让简单的sql变得更简单),倘若sql需要入参,或者当数据库字段名和实体类变量名不匹配需要resultMap来解决的时候,使用注解会格外复杂。所以mybatis这个框架不用注解,只用配置文件,而其他框架都推荐用注解。
6.1 使用注解实现增删改查
public interface UserMapper {
@Select("select * from user")
List<User> getAllUser();
@Insert("insert into `user` values(#{id}, #{username}, #{password})")
void addUser(User user);
@Update("update `user` set username=#{username},password=#{password} where id=#{id}")
void updateUser(User user);
@Delete("delete from `user` where id = #{id}")
void deleteUser(@Param("id") String id);
}
@Test
public void test01(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//1. --------------查
/*List<User> allUser = mapper.getAllUser();
for(User u:allUser){
System.out.println(u.toString());
}*/
//2.---------------增
// mapper.addUser(new User("44","newUser","88888"));
// sqlSession.commit();
//
// List<User> allUser = mapper.getAllUser();
// for(User u:allUser) {
// System.out.println(u.toString());
// }
//3.---------------改
// mapper.updateUser(new User("44","7/4","李涵颖去淄博烧烤"));
// sqlSession.commit();
//
// List<User> allUser = mapper.getAllUser();
// for(User u:allUser) {
// System.out.println(u.toString());
// }
//4.--------------删
mapper.deleteUser("44");
sqlSession.commit();
List<User> allUser = mapper.getAllUser();
for(User u:allUser) {
System.out.println(u.toString());
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
七、mybatis的执行流程
之前我们跟着教程创建mybatis工具类,写Mapper.xml,写Mapper接口,就可以执行SQL语句并返回结果了,那么mybatis究竟是怎么做到的呢?
通过打断点,一步步debug看变量总结出:
粗框部分是我们写代码可见的,其他是底层帮我们去做的。
八、多对一、一对多情况下mybatis如何应对
8.1 多个学生对一个老师
创建学生表和老师表,5个学生对应同一个老师。
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');
创建实体类Student,Teacher,创建对应的mapper接口和mapper.xml文件并绑定到mybatis核心配置文件中。
=============Student.java==============
@Data
public class Student {
int id;
String name;
Teacher teacher; //学生对应的老师
}
=============Teacher.java==============
@Data
public class Teacher {
int id;
String name;
//老师下面暂时没有对应的学生
}
=============StudentMapper.java==============
public interface StudentMapper {
public List<Student> getStudent();
}
=============TeacherMapper.java==============
public interface TeacherMapper {
Teacher getTeacherById(@Param("id") int id);
}
测试环境搭建好了。现在我们要查“所有学生以及学生对应的老师是谁”,是多对一
法(一) 基于子查询的嵌套——不推荐使用 (难)
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.StudentMapper">
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="pojo.Student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<!--对象:association 集合:collection-->
<association property="teacher" column="tid" javaType="pojo.Teacher" select="getTeacherById"></association>
</resultMap>
<select id="getTeacherById" resultType="pojo.Teacher">
select * from teacher where id = #{id}
</select>
</mapper>
结果如下:
法(二)SQL查询结果嵌套
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.StudentMapper">
<select id="getStudent" resultMap="StuTeaMap">
select s.id as sid, s.name as sname, t.name as tname
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StuTeaMap" type="pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="pojo.Teacher"> <!--Student的teacher属性是Teacher类型的-->
<result property="name" column="tname"/> <!--Teacher类的name属性对应tname字段-->
</association>
</resultMap>
</mapper>
8.2 一个老师对应多个学生
首先也是环境搭建,与上面的唯一不同就在于实体类的定义上。学生只需要记录老师的id即可,老师记录自己教的学生列表。
@Data
public class Student {
int id;
String name;
//Teacher teacher;
int tid; //现在学生只需要记录老师的id即可
}
@Data
public class Teacher {
int id;
String name;
List<Student> studentList;
}
现在我们要根据id查老师,并查出老师管理的学生都有谁,我们希望的结果是这样的
Teacher(id=1, name=瓜老师, studentList=[Student(id=1, name=小明, tid=1), Student(id=2, name=小赵, tid=1), Student(id=3, name=小牛, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小子, tid=1)])
法(一):按结果嵌套查询。
teacherMapper.xml
<mapper namespace="dao.TeacherMapper">
<select id="getTeacherById" resultMap="TeaStuMap">
select t.id as tid, t.name as tname, s.id as sid ,s.name as sname
from teacher t,student s
where t.id = s.tid AND t.id=#{id}
</select>
<resultMap id="TeaStuMap" type="pojo.Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<collection property="studentList" ofType="pojo.Student">
<!--注意这里是ofTyoe,不是javaType。如果用javaType的话,应该填“List”,因为
studentList的java类型就是List,但我们想要的是List里面元素的类型-->
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tid" column="tid"></result>
</collection>
</resultMap>
</mapper>
注意:ofType和javaType的区别。
结果:
法(二):子查询嵌套
不用也不想写了。
九、动态SQL
动态SQL:之前在mybatis里写的SQL语句都是我们一气呵成写完的。mybatis的强大之处在可以根据条件拼接SQL,叫做动态SQL
9.1 学习环境搭建
先建立一个数据库表
CREATE TABLE `blog`(
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
)ENGINE=InnoDB DEFAULT CHARSET =utf8
然后我们新建一个项目,利用mybatis向表里插入数据。
==============Blog.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Blog {
private String id; //博客id
private String title; //博客标题
private String author; //博客作者
private Date createTime; //1.注意这里要用java.util.Date而不是java.sql.Date
//2.数据库里是用下划线分割“create_time”,而java里我们习惯用的是驼峰规则。当然可以通过resultmap实现映射,但是还有一个更简单的方法是直接在settings配置里开启转换
private int views; //浏览量
}
============mybatis核心配置文件开启驼峰转换
<configuration>
......
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
......
</configuration>
==========BlogMapper.java
public interface BlogMapper {
int addBlog(Blog blog);
}
===========BlogMapper.xml
<mapper namespace="dao.BlogMapper">
<insert id="addBlog" parameterType="pojo.Blog">
insert into blog
values(#{id},#{title},#{author},#{createTime},#{views})
</insert>
</mapper>
===========新增一个生成UUTD的工具类
public class UUIDutils {
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-","");// 生成没有短横杠的UUID
}
}
=============测试类
@Test
public void test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog01 = new Blog(UUIDutils.getUUID(),"Mybatis如此简单","author",new Date(),888);
Blog blog02 = new Blog(UUIDutils.getUUID(),"spring如此简单","author",new Date(),765);
Blog blog03 = new Blog(UUIDutils.getUUID(),"springMVC如此简单","author",new Date(),2351);
Blog blog04 = new Blog(UUIDutils.getUUID(),"springboot如此简单","author",new Date(),34);
mapper.addBlog(blog01);
mapper.addBlog(blog02);
mapper.addBlog(blog03);
mapper.addBlog(blog04);
sqlSession.commit(); //提交!
sqlSession.close();
}
注意01:企业里数据库表的id不会手动添加,都是用UUID随机生成。因为如果是手动添加的话,比如添加了从id=1到id=9的9个用户,然后删掉了id=7的用户,那么这个表里再也不会出现id=7的用户,除非你insert进去一个。
注意02:在<settings>里开启带短横杠数据库字段名向驼峰命名的转换
9.2 动态SQL之IF语句
需求:写一个查找博客的接口,如果参数传入title,就按title去查,如果传入作者,就按作者去查,如果二者都有,就用AND去查,如果都没有,就查出来全部博客。这就用到了动态SQL语句!
==========新增查询接口
public interface BlogMapper {
List<Blog> queryBlogs(Map map);
}
==========BlogMapper.xml新增
<select id="queryBlogs" parameterType="map" resultType="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>
============测试类
@Test
public void test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
//1.查询条件title和author都有
map.put("title","mybatis如此简单");
map.put("author","author");
//2.查询条件:只有title
//map.put("title","mybatis如此简单");
//3.查询条件,只有author
//map.put("author","author");
List<Blog> blogs = mapper.queryBlogs(map);
for(Blog b:blogs){
System.out.println(b.toString());
}
sqlSession.close();
}
无论查询条件是否有title,是否有author,都能用queryBlog这一个接口查。
9.3 <where>标签
第一个<if>标签的例子其实有个弊端
那<where>标签就是来解决这个问题的:
- 当所有查询条件都为null时,<where>标签会帮我们删除where子句
- 当AND开头的查询条件被迫开头时,<where>标签会帮我们删除“AND”
9.4 <choose> <when> <otherwise>标签
需求:当查询条件有A时,我们直接用A去查,当没有A但有B时,我们用B去查;当A也有,B也有的时候,我们只用A去查(其实就是A的优先级高于B),二者都没有的时候我们设置一个“保底”的条件去查。和switch语句的含义一样。
=============BlogMapper.java新增接口
public interface BlogMapper {
List<Blog> queryBlogsChoose(Map map);
}
==============BlogMapper.xml
<select id="queryBlogsChoose" parameterType="map" resultType="pojo.Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != author">
AND author = #{author}
</when>
<otherwise>
views > 200
</otherwise>
</choose>
</where>
</select>
9.5 <set>标签
作用:同<where>标签。<set>标签用于update语句,会自动加上set关键字,并且会自动删去无关的逗号
9.6 <where>和<set>的爸爸——<trim>标签
相当于<where>,注意“AND|OR”是前缀
<trim prefix="where" prefixOverrides="AND|OR"></trim>
相当于<set>,注意“,”是SQL语句的后缀
<trim prefix="set" suffixOverrides=","></trim>
9.7 <foreach>标签
作用:对子查询(IN条件语句)得到的集合做遍历。
适合场景:因为下面的id可以有多个,在java程序里我们是通过ArrayList给出的,所以我们需要写成动态代码。如何拆分括号里的集合呢?
select * from blog where 1=1 AND (id=1 or id=2 or id=3 or id=.....)
为了测试方便,我们将博客的id改成了1,2,3,4.
9.8 <sql>提取sql片段
作用:将常用的sql片段提出出来,实现代码复用。在需要使用的地方用<include>标签引入<sql>代码片段。
注意:一般我们提取的都是<if>标签的一系列判断语句,不要把提取范围扩大到<where>和<set>标签包括的范围,不然实现不了复用。
十、mybatis缓存
使用场景:经常查询但不怎么改变的数据
一级缓存和二级缓存:mybatis默认开启一级缓存,而且关不掉,也叫sqlsession级别的缓存,仅对一次会话产生的数据进行缓存。二级缓存的作用范围比一级更大,其作用域是namespace,如果觉得一级缓存还不够,想要开启二级缓存,就要用到<cache/>标签。
10.1 一级缓存
一级缓存测试:
开启日志,用标准日志工厂就行
一旦缓存达到阈值,就会开启缓存清除策略:
- LRU最近最少使用:移除最长时间不被使用的对象 (√ 默认策略)
- FIFO:先进先出
- 。。。
一级缓存失效的几种情况:
- 第一次查和第二次查不同东西,当然用不到的缓存
- 一旦有了增删改操作,必定会刷新缓存。比如在同一个sqlSession里先查id=1的博客,然后插入了一条新博客,再去查id=1的博客的时候不走缓存。
创建sqlSession
.....
mapper.getBlog(1);
mapple.addBlog(new Blog(4,"后面属性不写了")); //插入操作会更新缓存
mapper.getBlog(1); //这里不走缓存!
....
sqlSession.close();
- 查询不同的Mapper.xml肯定不走缓存
- 手动清理缓存
10.2 二级缓存
- 二级缓存也叫全局缓存,一级缓存的作用域太低了,所以诞生了二级
- 基于namespace级别的缓存
- 工作机制
- 一个sqlSession会话查询的数据被放在这个session的以及缓存中
- 当这个session关闭了,对应的一级缓存也就没了。但是我们想要的是,会话关闭以后,一级缓存的数据保存到二级缓存中
- 此后开启的新的session可以从二级缓冲中获取已经“死掉”的session的数据
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
- 如何开启二级缓存
1、在核心配置文件中显式地开启全局缓存
2、在mapper.xml文件中用<cache/>标签
3、测试
注意:两个会话必须访问的是同一个mapper,二级缓存才能生效
如果报错“NotSerilizable”,就把实体类序列化一下
10.3 缓存模型
当一个sqlsession创建了一个mapper去查询的时候,先去二级缓存里找,看看这个结果是不是被其他sqlSession之前查过,查过直接拿来用就行。如果二级缓存没找到,再去一级缓存里找,看看自己(本sqlSession)是不是之前搜索过,如果一级缓存里也没有,那就直接去访问数据库吧!
10.4 关闭缓存
如果一个字段被频繁更改,就可以设置查这个字段的时候不要从缓存当中读,而是每次都查数据库,也就是关闭缓存。
10.5 自定义缓存EHcache
- 一个第三方包,用的话要导入jar包。还要导一个xml配置文件
- 比二级缓存功能更强大,可以把缓存持久化到硬盘上。
- 但是现在被redis给淘汰了。