Mybatis复习与高频面试题
一、常见面试题
MyBatis是什么?
答:MyBatis是一个半ORM(对象关系映射)框架,内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。通过直接编写原生态SQL,可以严格控制SQL语句的执行性能,灵活度高(支持动态SQL语句)。MyBatis 使用XML或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了JDBC代码手动设置参数以及获取结果集的繁琐步骤。MyBatis通过xml文件或注解的方式将要执行的各种 statement 配置起来,并通过Java对象和 statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL语句,并将结果映射为Java对象并返回。
MyBatis的核心组件有哪些?
答: MyBatis的核心组件包括SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession和Mapper。在开头给出的Demo中,我们也可以看到这四大核心组件。
下边我们依次介绍。SqlSessionFactoryBuilder是一个构建器,通过XML配置文件或者Java编码获得资源来构建SqlSessionFactory,通过Builder可以构建多个SessionFactory。其生命周期一般只存在于方法的局部,用完即可回收。一般通过如下语句建立:
SqlSessionFactory factory = SqlSessionFactoryBuilder.build(inputStream);
SqlSessionFactory的作用就是创建SqlSession,也就是创建一个会话。每次程序需要访问数据库,就需要使用到SqlSession。所以SqlSessionFactory应该在MyBatis应用的整个生命周期中。为了减少每次都创建一个会话带来的资源消耗,一般情况下都会使用单例模式来创建SqlSession。创建方式如下:
SqlSession sqlSession = SqlSessionFactory.openSession();
SqlSession就是一个会话,相当于JDBC中的Connection对象,既可以发送SQL去执行并返回结果,也可以获取Mapper接口。SqlSession是一个线程不安全的对象,其生命周期应该是请求数据库处理事务的过程中。每次创建的SqlSession对象必须及时关闭,否则会使得数据库连接池的活动资源减少,影响系统性能。Mapper也叫做映射器,由Java接口和XML文件(或者是注解)共同组成,给出了对应的SQL和映射规则,主要负责发送SQL去执行,并且返回结果。通过下边的语句来获取Mapper,接下来就可以调用Mapper中的各个方法去获取数据库结果了。
XXMapper xxMapper = sqlSession.getMapper(XXMapper.class);
解析:
四大核心组件是MyBatis框架的重要组成部分,必须熟练掌握使用并且了解组件作用。根据Demo展示,我们看到Test测试类中创建SqlSessionFactory的过程首先是通过读取xml配置,然后使用SqlSessionFactorybuilder来build一个SqlSessionFactory。具体创建过程的步骤如下:通过XMLConfigBuilder解析配置的XML文件,读出配置参数,封装到Configuration类中使用Configuration对象去创建SqlSessionFactory,SqlSessionFactory在MyBatis中是一个接口而不是一个类,所以提供了一个其默认实现类DefaultSqlSessionFactory创建SqlSessionFactory的本质是一种Builder模式。Configuration的主要作用如下:读入配置文件初始化基础配置。包括全局配置,别名,类型处理器,环境数据库标识以及Mapper映射器等提供单例执行一些重要的对象方法和初始化配置信息
Mybatis 缓存
定义:Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存
是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以
后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。二级缓存
是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的
sqlsession 是可以共享的。
Mybatis 的一级缓存原理(sqlsession 级别:
1、第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一
个 map。
- key:MapperID+offset+limit+Sql+所有的入参
- value:用户信息
2、同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作
(修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所
以要从数据库查询,从数据库查询到再写入缓存。
二级缓存原理(mapper 基本):
1、二级缓存的范围是 mapper 级别(mapper 同一个命名空间),mapper 以命名空间为单位创建缓
存数据结构,结构是 map。mybatis 的二级缓存是通过 CacheExecutor 实现的。CacheExecutor其实是 Executor 的代理对象。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。
- key:MapperID+offset+limit+Sql+所有的入参
具体使用需要配置:
- Mybatis 全局配置中启用二级缓存配置
- 在对应的 Mapper.xml 中配置 cache 节点
- 在对应的 select 查询节点中添加 useCache=true
二、Mybatis的参数深入
搭建demo
- sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) NULL DEFAULT NULL COMMENT '用户编号',
`MONEY` double NULL DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`) USING BTREE,
INDEX `FK_Reference_8`(`UID`) USING BTREE,
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES (1, 46, 1000);
INSERT INTO `account` VALUES (2, 45, 1000);
INSERT INTO `account` VALUES (3, 46, 2000);
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`ID` int(11) NOT NULL COMMENT '编号',
`ROLE_NAME` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
`ROLE_DESC` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '院长', '管理整个学院');
INSERT INTO `role` VALUES (2, '总裁', '管理整个公司');
INSERT INTO `role` VALUES (3, '校长', '管理整个学校');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名称',
`birthday` datetime(0) NULL DEFAULT NULL COMMENT '生日',
`sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
`address` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 73 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (41, '老王', '2018-02-27 17:47:08', '男', '北京');
INSERT INTO `user` VALUES (42, '小二王', '2018-03-02 15:09:37', '女', '北京金燕龙');
INSERT INTO `user` VALUES (43, '小二王', '2018-03-04 11:34:34', '女', '北京金燕龙');
INSERT INTO `user` VALUES (45, '传智播客', '2018-03-04 12:04:06', '男', '北京金燕龙');
INSERT INTO `user` VALUES (46, '老王', '2018-03-07 17:37:26', '男', '北京');
INSERT INTO `user` VALUES (48, '小马宝莉', '2018-03-08 11:44:00', '女', '北京修正');
INSERT INTO `user` VALUES (53, '司理理', '2020-04-14 00:22:25', '女', '北齐');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`UID` int(11) NOT NULL COMMENT '用户编号',
`RID` int(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (`UID`, `RID`) USING BTREE,
INDEX `FK_Reference_10`(`RID`) USING BTREE,
CONSTRAINT `FK_Reference_10` FOREIGN KEY (`RID`) REFERENCES `role` (`ID`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`UID`) REFERENCES `user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (41, 1);
INSERT INTO `user_role` VALUES (45, 1);
INSERT INTO `user_role` VALUES (41, 2);
SET FOREIGN_KEY_CHECKS = 1;
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jp</groupId> <artifactId>review</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies></project>
mybatis的配置文件SqllMapConfig.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">
<!-- 之前的SqlMapConfig.xml中property标签配置的数据库信息可配置在properties标签中,并使用${}引用-->
<configuration>
<typeAliases>
<typeAlias type="com.jp.domain.User" alias="user"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<!-- 事务型类型为jdbc-->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源配置-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://139.9.141.121:3306/2020offer?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 一、利用xml配置-->
<!-- 配置映射-->
<mappers>
<!-- 方法一\resource值是映射的xml文件位置-->
<!-- <mapper resource="dao/IUserDao.xml"/>-->
<!-- 方法二package映射xml的文件位置-->
<package name="com/jp/dao"/>
</mappers>
<!--<!– 二、利用注解配置–>-->
<!-- <mappers>-->
<!--<!– 使用注解配置时,设置class的值为dao接口全限定类名,在使用基于注解的 Mybatis 配置时,请移除 xml 的映射配置(IUserDao.xml)。–>-->
<!-- <mapper class="com.jp.dao.IUserDao"/>-->
<!-- </mappers>-->
</configuration>
1. ParameterType
- 传递简单类型
- 传递pojo对象
Mybatis使用ognl表达式解析对象字段的值,#{}或者${}中的值为pojo属性名称 - 传递pojo包装对象
开发中通过 pojo 传递查询条件 ,查询条件是综合的查询条件,不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。Pojo 类中包含 pojo。
案例:根据用户名查询用户信息,查询条件放到 QueryVo 的 user 属性中。
定义包装类QueryVo
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
2.实体属性名和数据库列名不同的解决:resultMap 结果类型(开发效率高)
resultMap 标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系。从而实现封装。 在 select 标签中使用resultMap属性指定引用即可。
同时 resultMap 可以实现将查询结果映射为复杂类 型的 pojo,比如在查询结果映射对象中包括 pojo 和 list 实现一对一查询和一对多查询。
- 定义resultMap
<!-- 建立 User 实体和数据库表的对应关系
type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。-->
<resultMap id="userMap" type="com.jp.domain.User"> <id column="id" property="userId"/> <result column="username" property="userName"/> <result column="sex" property="userSex"/> <result column="address" property="userAddress"/> <result column="birthday" property="userBirthday"/> </resultMap>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property属性:用于指定实体类属性名称
3、使用别名(执行效率高)
<select id="findAllAlians" resultType="com.jp.domain.UserTest">
select id as userId,username as userName,birthday as
userBirthday,address as userAddress,sex as userSex from user;
</select>
typeAliases标签
typeAlias使用该标签,可给实体类起别名,不需要在配置时再写全限定类名
Mybatis 的连接池技术与事务控制
可以看出 Mybatis 将它自己的数据源分为三类:
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源JNDI 使用 JNDI 实现的数据源
具体结构如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYXCqRSf-1587572579394)(en-resource://database/579:1)]
相应地,MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource,
PooledDataSource 类来表示 UNPOOLED、POOLED 类型的数据源
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术)。
我们的数据源配置就是在 SqlMapConfig.xml 文件中,具体配置如下:
<!-- 配置数据源(连接池)信息 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
MyBatis 在初始化时,根据的type属性来创建相应类型的的数据源 DataSource,即:
type=“POOLED”:MyBatis 会创建 PooledDataSource 实例
type=“UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例
type=“JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
事务控制
在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以调整。
Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的setAutoCommit()方法来设置事务提交方式的。
当使用sqlSession工厂的openSession方法时,如果空参或false代表关闭事务的自动提交,如果传入true代表打开事务自动提交。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VI4p6Mv2-1587572579396)(en-resource://database/581:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YjWWXOg-1587572579396)(en-resource://database/583:1)]
Mybatis的多表关联查询
account表建立
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);
通过编写Account子类实现多表一对一查询
通过建立实体类关系实现多表一对一查询
account添加user属性
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//通过建立实体类关系实现多表一对一查询
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
配置映射
<!-- 方法二-->
<!-- association指定类中引用的类属性:-->
<resultMap id="map" type="com.jp.domain.Account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<association property="user" column="uid" javaType="com.jp.domain.User">
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
</association>
</resultMap>
<select id="findUserAndAccount" resultMap="map">
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid=u.id
</select>
测试方法
@Test
public void findUserAndAccount2() {
List<Account> list = accountDao.findUserAndAccount();
for (Account account : list) {
System.out.println(account);
System.out.println(account.getUser());
System.out.println("_____________");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cevfet11-1587572579397)(en-resource://database/587:1)]
多表一对多查询
需求:查询所有的用户信息及用户关联的账户信息
用户和账户是一对多的关系
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
IUserDao.xml映射配置
<resultMap id="map" type="com.jp.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<!-- 配置user对象中account集合的映射-->
<collection property="accounts" ofType="com.jp.domain.Account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>
<select id="findAllTest1toN" resultMap="map">
SELECT u.*,a.id as aid,a.UID,a.MONEY from user u left join account a on u.id=a.UID
</select>
- collection:部分定义了用户关联的账户信息。表示关联查询结果集
- property=“accounts”:关联查询的结果集存储在 User 对象的上哪个属性。
- ofType=“account”:指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名
多表多对多的查询
如下表所示 用户与角色是多对多的关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0j97DQN-1587572579399)(en-resource://database/591:1)]
角色role类
public class Role {
private Integer id;
private String roleName;
private String roleDesc;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDesc() {
return roleDesc;
}
public void setRoleDesc(String roleDesc) {
this.roleDesc = roleDesc;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
角色User类
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Role> roles;
private List<Account> accounts;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
映射配置
<resultMap id="mapUserRoles" type="com.jp.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<collection property="roles" ofType="com.jp.domain.Role">
<id property="id" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
</collection>
</resultMap>
<select id="findUserRole" resultMap="mapUserRoles">
select u.*,r.ID as rid,r.ROLE_NAME,r.ROLE_DESC from user u left join user_role ur on u.id=ur.uid left join role r on r.id=ur.RID;
</select>
@Test
public void findUserRoles() {
List<User> list = userDao.findUserRole();
for (User user : list) {
System.out.println(user);
System.out.println(user.getRoles());
System.out.println("---------");
}
}
Mybatis的延迟加载
实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。
-
延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载. -
好处:
先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。 -
坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降
Mybatis缓存
- 概念
存在于内存中的临时数据使用的目的
- 优点
- 减少和数据库的交互次数,提高执行效率适用于缓存的数据
- 经常查询且不常改变的,数据的正确性对最终结果影响不大。
一级缓存
指的是Mybatis中SqlSession对象的缓存,当我们执行完查询结果后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当我们再次查询同样数据时,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,Mybatis的一级缓存也就消失了。
- 一级缓存的清除
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。