目录
一、初步配置Mybatis及一个简单测试
Mybatis是应用在持久层的框架,大大简化jdbc的代码操作,隐藏了大量的细节
1、利用Maven引入依赖
-
mysql依赖
-
mybatis依赖
2、配置Mybatis的xml文件
如果是maven工程,在resource文件夹中创建一个mybatis的xml配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
</configuration>
*核心内容是数据库的四个属性值
3、获取SqlSession的实例
- Mybatis框架提供了一个SqlSessionFactory接口,它的一个实现类SqlSession中有对数据库的增删改查等操作,以下建立一个工具类作为获取实例的类
package com.kuang.utils;
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;
//sqlSessionFactory -->sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//使用Mybatis第一步:获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
try {
InputStream is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/*既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
4、建立对象类进行测试
- 以下为建立的User类,用于下一步测试
package com.kuang.pojo;
public class User {
private Integer id;
private String name;
private String pwd;
public User(Integer id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
- 写一个持久层的接口
-
package com.kuang.dao; import com.kuang.pojo.User; import java.util.List; public interface UserMapper { List<User> getUserList(); }
注意:传统代码式jdbc操作中,需要创建一个UserMapper的实现类,在实现类中实现上代码段中的getUserList()方法,而在Mybatis中,使用一个xml配置文件代替实现类,如下
<?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.kuang.dao.UserMapper">
<!-- 查询语句-->
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from user;
</select>
</mapper>
此文件也需要在Mybatis的配置文件中配置,配置格式见本文第一张图片的<mappers>标签
最后,编写测试类
package com.kuang.dao;
import com.kuang.pojo.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void test(){
//获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:执行SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user :
userList) {
System.out.println(user);
}
sqlSession.close();
}
}
结果如下:
User{id=1, name='狂神', pwd='123456'}
User{id=2, name='张三', pwd='123456'}
User{id=3, name='李四', pwd='123456'}
如有疑问详情访问Mybatis的官方中文文档:
二、CRUD操作
1、插入
//插入一个用户 int addUser(User user);
UserMapper.xml文件中配置如下
@Test
public void testAddUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int reuslt = mapper.addUser(new User(null, "韩阳", "1999225a"));
System.out.println("影响了:"+reuslt+"条语句.");
sqlSession.commit();
sqlSession.close();
}
2、步骤总结
- 编写接口
- 在对应xml文件中编写sql
- 测试
注意:
- 插入操作时,应加入一条语句对事务进行提交,否则,插入不会生效
- sqlSessoin.commit();
- #{id} 中的id字段,为pojo包中User类属性
3、Map和模糊查询拓展
假设我们的实体类或者数据库中的表,字段过多,应当考虑使用Map
- 编写接口
//万能Map int addUserByMap(Map<String,Object> map);
2、编写xml
<insert id="addUserByMap" parameterType="Map"> insert into user values (#{id},#{username},#{password}); </insert>
3、编写测试
@Test
public void testAddUserByMap(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id",null);
map.put("username","李勇");
map.put("password","123789a");
int i = mapper.addUserByMap(map);
if (i>0){
System.out.println("插入成功");
}
sqlSession.commit();
sqlSession.close();
}
Map传递参数,直接在sql中取key即可
parameterType="Map"
对象传递参数,直接在sql中取对象属性
parameterType="Object"
只有一个基本类型参数的情况下,可以直接在sql中取到
多个参数用Map或者注解
三、配置解析
1、核心配置文件
- mybatis-config.xml
- Mybatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
2、环境配置(environments)
MyBatis可以配置成适应多种环境
但是每个SqSessionFactory实例只能选择一个环境
Mybatis默认的事务管理器是JDBC,连接池:POOL
3、属性(properties)
我们可以通过该properties属性实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的java属性配置文件配置,亦可以通过properties元素的子元素来传递
在xml中,约定properties必须放在第一个
1、创建jdbc.properties
username=root password=root driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=truez
在核心配置文件中引入
<properties resource="jdbc.properties"/>
4、类型别名(typeAliases)
<!-- 给实体类起别名--> <typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>
<!-- 查询语句--> <select id="getUserList" resultType="User"> select * from mybatis.user; </select>
也可以指定一个包名,Mybatis会在包名下面搜索需要的JavaBean,比如:扫描实体类的包,它的默认别名就为这个类的类名首字母小写。
<!-- 给实体类起别名--> <typeAliases> <package name="com.kuang.pojo"/> </typeAliases>
<!-- 查询语句--> <select id="getUserList" resultType="user"> select * from mybatis.user; </select>
在扫描包的情况下,也可以用注解@Alias起别名
总结:
如果用 typeAlias标签给一个类起别名,而不指定alias="xx"属性时,以及用包来指定别名时,别名都是不区分大小写的类名
5、 映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
方式一:【推荐】
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!-->
<mappers>
<mapper resource="com.kuang.dao.UserMapper"/>
</mappers>
注意:
- 接口和它的mapper配置文件必须同名
- 接口和它的配置文件必须在同一包下
方式三:使用扫描包进行注入绑定
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册!-->
<mappers>
<!-- <mapper resource="com/kuang/dao/UserMapper.xml"/>-->
<package name="com.kuang.dao"/>
</mappers>
注意:
- 接口和它的mapper配置文件必须同名
- 接口和它的配置文件必须在同一包下
四、解决属性名和字段名不一致的问题
数据库中的字段
测试
public class User {
private Integer id;
private String name;
private String password;
<!-- 查询语句-->
<select id="getUserById" resultType="user" parameterType="int">
select * from user where `id`=#{id};
</select>
public class UserMapperTest {
@Test
public void test(){
//获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:执行SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
}
}
查询结果:
查出password为空
解决方案
- 在sql中给字段起别名,达到名称效果一致
<!-- 查询语句-->
<select id="getUserById" resultType="user" parameterType="int">
select id,name,pwd password from user where `id`=#{id};
</select>
- resultMap(结果集映射)
在对象类的xml中创建结果集映射
<resultMap id="UserMap" type="User">
<result column="username" property="username"/>
<result column="id" property="id"/>
<result column="pwd" property="password"/>
</resultMap>
<!-- 查询语句-->
<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from user where `id`=#{id};
</select>
resultMap是Mybatis中最重要最强大的元素
resultMap的设计思想是:对于简单的语句根本不需要配置显示的结果映射,而对于复杂一点的语句只要描述它们的关系就行了
五、日志
1、log4j
- log4j是一个apche的开源项目,可以控制日志信息的输送目的地是控制台、文件GUI组件
- 可以控制日志的输出格式
- 通过定义每条日志信息的级别,我们能更细致地控制日志的生成过程
- 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
2、使用
- 先导入log4j的依赖
- 设置log4j为日志的实现
<settings>
<!-- 标准日志工厂-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- log4j-->
<setting name="logImpl" value="LOG4J"/>
</settings>
六、分页
- 接口
List<User> getUserByLimit(Map<String,Integer> map);
-
UserMapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select * from user limit #{startIndex},#{pageSize}; </select>
- 测试
@Test public void testGetUserByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Integer> map = new HashMap<>(); map.put("startIndex",0); map.put("pageSize",3); List<User> userByLimit = mapper.getUserByLimit(map); for (User user : userByLimit) { System.out.println(user); } }
七、使用注解开发
1、查询
1、注解在接口上实现
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
2、需要在核心配置文件中绑定接口(仅绑定一次)
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
3、测试
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//底层主要应用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
}
本质:反射机制
底层:动态代理
当方法中有多个参数时,用注解@Param来标注对应参数
@Select("select * from user where id=#{id}")
User getUserById(@Param("id")int id);
2、添加
1、在接口上添加注解
@Insert("insert into user values(null,#{name},#{password})")
int addUser(User user);
2、测试
@Test
public void testAddUser(){
int i = MybatisUtils.getSqlSession().getMapper(UserMapper.class).addUser(new User(null, "盲僧", "mangseng666"));
if (i>0){
System.out.println("添加成功!!!");
}
}
修改、 删除同理
关于@Param注解
- 基本类型参数或者String类型 需要加上
- 引用类型不需要加
- 如果只有一个基本类型,可以忽略
- 我们在SQL中引用的就是我们在注解中设置的属性名
关于#{}和${}
- #{} 用来传入参数时,sql解析时会加上" ",能够很大程度上防止sql注入
- ${} 是直接显示在生成的sql中,无法防止sql注入
- 能用#{}尽量用
八、Lombok
*java插件
安装步骤:
*在idea中选择settings--->Plugins--->搜索lombok安装即可
*在项目中导入lombok的依赖
*功能:利用注解自动生成get,set,hashcode,equals,构造器等
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data 生成get,set,hashcode,equals,toString,无参构造【常用】
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
@ExtensionMethod (Experimental, activate manually in plugin settings)
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
九、多对一的处理
创建一个多对一的模型
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);
1、按照查询嵌套处理
实体类如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias(value = "tea")
public class Teacher {
private int id;
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias(value = "stu")
public class Student {
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
}
需求:
public interface StudentMapper {
//查询所有学生信息及对应老师
public List<Student> getStu();
}
查询方式如下:
<!--
思路:
1、查询所有学生信息
2、根据查询出来的学生tid,查找对应老师
-->
<select id="getStu" resultMap="StuTea">
SELECT * FROM student;
</select>
<resultMap id="StuTea" type="stu">
<!-- 复杂的属性,要单独处理 对象:association 集合:collection-->
<association property="teacher" column="tid" javaType="tea" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="tea">
select * from teacher where id = #{id};
</select>
2、按照结果嵌套处理
<!-- 按照结果嵌套处理-->
<select id="getStu2" resultMap="StuTea2">
SELECT s.id sid,s.name sname,t.name tname FROM student s,teacher t WHERE s.tid=t.id;
</select>
<resultMap id="StuTea2" type="stu">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<association property="teacher" javaType="tea">
<result property="name" column="tname"/>
</association>
</resultMap>
两种方式查询结果一样
十、一对多处理
一个老师拥有多个学生,对老师而言,就是一对多的关系
实体类如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias(value = "stu")
public class Student {
private int id;
private String name;
private int tid;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias(value = "tea")
public class Teacher {
private int id;
private String name;
//一个老师有多个学生
private List<Student> students;
}
1、按照查询嵌套处理
实体类如下:
//获取指定老师下所有学生和老师信息
Teacher getTeachers(@Param("tid") int id);
<!-- 子查询-->
<select id="getTeachers2" resultMap="TeaStu">
select * from teacher where id=#{tid};
</select>
<resultMap id="TeaStu" type="tea">
<collection property="students" javaType="ArrayList" ofType="stu" select="getStusByTeacherId" column="id"/>
</resultMap>
<select id="getStusByTeacherId" resultType="stu">
select * from student where tid=#{tid};
</select>
2、按照结果嵌套处理【推荐】
Teacher getTeachers2(@Param("tid") int id);
<!-- 按结果嵌套查询-->
<select id="getTeachers" resultMap="StuTea">
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="StuTea" type="tea">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- 复杂的属性,我们需要单独处理,对象:association 集合:collection
javaType 指定属性的类型
集合中的泛型,我们使用ofType获取
-->
<collection property="students" ofType="stu">
<result property="name" column="sname"/>
<result property="id" column="sid"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
小结
1、关联 association 【多对一】
2、集合 collection 【一对多】
3、javaType 指定实体类属性的类型
4、ofType 指定映射到集合中的泛型约束类型
十一、动态SQL
动态sql就是根据不同的条件生成不同的SQL语句
利用动态SQL这一特性可以彻底摆脱这种痛苦
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
建立环境
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
编写实体类如下
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String name;
private String title;
private String author;
private Date createTime;
private int views;
}
配置核心配置文件,设置为可以将经典sql和java驼峰命名字段的自动映射
<settings>
<!-- 标准日志工厂-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1、IF
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
2、choose
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
and author=#{author}
</when>
<otherwise>
and views=#{views}
</otherwise>
</choose>
</where>
</select>
原理与java中的switch-case相似,搭配where标签使用
3、trim、where、set
where标签可以去掉sql拼接时第一个多余的and
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
set标签去掉多余逗号
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author}
</if>
</set>
where id =#{id}
</update>
如果 where 元素和set元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
<trim prefix="SET" suffixOverrides=","> ... </trim>
4、SQL片段
有些时候,抽取相同的sql代码段进行复用,会用到以下操作抽取:
<!--抽取公共部分-->
<sql id="if-if">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</sql>
<!--在需要的地方用include标签引用即可-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-if"></include>
</where>
</select>
类似于静态包含
注意:
- 最好基于单表定义SQL片段
- 不要存在where标签
5、forEach
<select id="queryBlogForEach" parameterType="map" resultType="blog">
select * from blog
# 我们传递一个万能的map,map中可以存在一个集合
<where>
<foreach collection="ids" item="id" open="(" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>
测试代码:
@Test
public void testQueryBlogForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(4);
hashMap.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForEach(hashMap);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
十二、缓存
1、一级缓存
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
缓存失效的情况:
1、查询不同的东西
2、增删改操作,可能会改变原来的数据,缓存必定刷新
@Test
public void testQueryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
mapper.updateUser(new User(1,"地龙人","555"));
System.out.println("**********************************");
User user1 = mapper.queryUserById(1);
System.out.println(user1);
System.out.println(user==user1);
}
3、查询不同的Mapper.xml
4、手动清理缓存
@Test
public void testQueryUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
// mapper.updateUser(new User(1,"地龙人","555"));
sqlSession.clearCache();
System.out.println("**********************************");
User user1 = mapper.queryUserById(1);
System.out.println(user1);
System.out.println(user==user1);
}
小结:一级缓存默认是开启的,只在一段sqlSession中有效,也就是拿到连接到关闭连接这段时间
1、二级缓存
- 二级缓存也叫全局缓存,一级缓存作用于太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间对应一个二级缓存
- 工作机制
*一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
*如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
*新的会话查询信息,就可以从二级缓存中获取内容
*不同的mapper查出的数据会放在自己对应的缓存(map)中
要启用全局二级缓存
步骤一
- 开启全局缓存
<!-- 开启全局缓存,默认为true,这里显式声明--> <setting name="cacheEnable" value="true"/>
- 需要在SQL映射文件中添加一行:
<cache/ readOnly="true">
*关于readOnly属性
官方解释:
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。速度上会慢一些,但是更安全,因此默认值是 false。
测试:
@Test
public void testQueryUserById2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
sqlSession.close();
User user1 = mapper1.queryUserById(1);
sqlSession1.close();
System.out.println(user);
System.out.println(user1);
System.out.println(user == user1);
}
结果:
问题:
我们需要将实体类序列化,否则就会报错,对应上文readOnly属性参数
小结:
- 只要开启了二级缓存,在同一个mapper下有效
- 所有数据都会先放在一级缓存中
- 只有当会话提交,或者关闭,才会提交到二级缓存中