Mybatis缓存

2021-02-16 15:52:44,863 359 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection

2021-02-16 15:52:45,164 660 [ main] DEBUG source.pooled.PooledDataSource - Created connection 905735620.

2021-02-16 15:52:45,168 664 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 15:52:45,200 696 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 15:52:45,227 723 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@400cff1a

com.keafmd.domain.User@400cff1a

true

我们可以看到,只发起了一次查询,相当于第一次是查询,而第二次是从缓存中取得。

一级缓存的分析


一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

举个例子:

在这里插入图片描述

**第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。

得到用户信息,将用户信息存储到一级缓存中。

如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。**

测试一级缓存的清空


改变下测试代码:

/**

  • 测试一级缓存

*/

@Test

public void testFirstLevelCache(){

User user1 = userDao.findById(41);

System.out.println(user1);

/*sqlSession.close();

//再次获取sqlSession对象

sqlSession = factory.openSession();*/

sqlSession.clearCache();//此方法也可以清空缓存

userDao = sqlSession.getMapper(IUserDao.class);

User user2 = userDao.findById(41);

System.out.println(user2);

System.out.println(user1==user2);

}

运行结果:

2021-02-16 16:14:57,171 194 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection

2021-02-16 16:14:57,498 521 [ main] DEBUG source.pooled.PooledDataSource - Created connection 93314457.

2021-02-16 16:14:57,503 526 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 16:14:57,541 564 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 16:14:57,560 583 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@57175e74

2021-02-16 16:14:57,561 584 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 16:14:57,561 584 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 16:14:57,562 585 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@c540f5a

false

2021-02-16 16:14:57,563 586 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58fdd99]

2021-02-16 16:14:57,563 586 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 93314457 to pool.

Process finished with exit code 0

我们可以看到这样就发起了两次查询,我们用这两种方式都可以清空缓存。

添加updateUser方法

IUserDao:

package com.keafmd.dao;

import com.keafmd.domain.User;

import java.util.List;

/**

  • Keafmd

  • @ClassName: IUserDao

  • @Description: 用户的持久层接口

  • @author: 牛哄哄的柯南

  • @date: 2021-02-06 19:29

*/

public interface IUserDao {

/**

  • 查询所有用户,同时获取到用户下所有账户的信息

  • @return

*/

List findAll();

/**

  • 根据id查新用户信息

  • @param id

  • @return

*/

User findById(Integer id);

/**

  • 更新用户信息

  • @param user

*/

void updateUser(User user);

}

IUserDao.xml:

<?xml version="1.0" encoding="UTF-8"?>

select * from user

select * from user where id = #{id}

update user set username=#{username},address=#{address} where id=#{id}

测试缓存的同步

UserTest:

package com.keafmd.test;

import com.keafmd.dao.IUserDao;

import com.keafmd.domain.User;

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 org.junit.After;

import org.junit.Before;

import org.junit.Test;

import java.io.InputStream;

import java.util.List;

/**

  • Keafmd

  • @ClassName: MybatisTest

  • @Description: 测试类

  • @author: 牛哄哄的柯南

  • @date: 2021-02-08 15:24

*/

public class UserTest {

private InputStream in;

private SqlSessionFactory factory;

private SqlSession sqlSession;

private IUserDao userDao;

@Before // 用于在测试方法执行前执行

public void init()throws Exception{

//1.读取配置文件,生成字节输入流

in = Resources.getResourceAsStream(“SqlMapConfig.xml”);

//2.创建SqlSessionFactory工厂

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

factory = builder.build(in);

//3.使用工厂生产SqlSession对象

sqlSession = factory.openSession(true); //里面写个true,下面每次就不用了写 sqlsession.commit(); 了

//4.使用SqlSession创建Dao接口的代理对象

userDao = sqlSession.getMapper(IUserDao.class);

}

@After // 用于在测试方法执行后执行

public void destory() throws Exception{

//提交事务

//sqlSession.commit();

//6.释放资源

sqlSession.close();

in.close();

}

/**

  • 测试一级缓存

*/

@Test

public void testFirstLevelCache(){

User user1 = userDao.findById(41);

System.out.println(user1);

/*sqlSession.close();

//再次获取sqlSession对象

sqlSession = factory.openSession();*/

sqlSession.clearCache();//此方法也可以清空缓存

userDao = sqlSession.getMapper(IUserDao.class);

User user2 = userDao.findById(41);

System.out.println(user2);

System.out.println(user1==user2);

}

/**

  • 测试缓存的同步

*/

@Test

public void testClearCache(){

//1.根据id查询用户

User user1 = userDao.findById(41);

System.out.println(user1);

//2.更新用户信息

user1.setUsername(“update user clear cache”);

user1.setAddress(“上海”);

userDao.updateUser(user1);

//手动提交

sqlSession.commit();

//3.再次查询id为41的用户

User user2 = userDao.findById(41);

System.out.println(user2);

System.out.println(user1==user2);

}

}

运行testClearCache():

2021-02-16 16:13:04,277 209 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection

2021-02-16 16:13:04,560 492 [ main] DEBUG source.pooled.PooledDataSource - Created connection 93314457.

2021-02-16 16:13:04,565 497 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 16:13:04,618 550 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 16:13:04,645 577 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@57175e74

2021-02-16 16:13:04,646 578 [ main] DEBUG keafmd.dao.IUserDao.updateUser - ==> Preparing: update user set username=?,address=? where id=?

2021-02-16 16:13:04,646 578 [ main] DEBUG keafmd.dao.IUserDao.updateUser - ==> Parameters: update user clear cache(String), 上海(String), 41(Integer)

2021-02-16 16:13:04,647 579 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 16:13:04,648 580 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 16:13:04,649 581 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@c540f5a

false

2021-02-16 16:13:04,650 582 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58fdd99]

2021-02-16 16:13:04,650 582 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 93314457 to pool.

Process finished with exit code 0

我们在第一次查询后进行的更新操作,所以会清空一级缓存,然后第二次会在进行查询。

Mybatis二级缓存

=====================================================================

**二级缓存:

它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存的使用步骤:

第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

第三步:让当前的操作支持二级缓存(在select标签中配置)

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。**

二级缓存结构图


在这里插入图片描述

**首先开启 mybatis 的二级缓存。

sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交,将会清空该 mapper 映射下的二级缓存区域的数据。

sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。**

二级缓存的开启与关闭


第一步:在 SqlMapConfig.xml 文件开启二级缓存

因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。

第二步:配置相关的 Mapper 映射文件


IUserDao.xml:

<?xml version="1.0" encoding="UTF-8"?>

select * from user

select * from user where id = #{id}

update user set username=#{username},address=#{address} where id=#{id}

第三步:配置 statement 上面的 useCache 属性

select * from user where id = #{id}

**将 UserDao.xml 映射文件中的标签中设置 useCache=”true”代表当前这个statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。**

二级缓存测试


SecondLevelCacheTest:

package com.keafmd.test;

import com.keafmd.dao.IUserDao;

import com.keafmd.domain.User;

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 org.junit.After;

import org.junit.Before;

import org.junit.Test;

import java.io.InputStream;

/**

  • Keafmd

  • @ClassName: MybatisTest

  • @Description: 测试类,测试crud操作

  • @author: 牛哄哄的柯南

  • @date: 2021-02-08 15:24

*/

public class SecondLevelCacheTest {

private InputStream in;

private SqlSessionFactory factory;

@Before // 用于在测试方法执行前执行

public void init()throws Exception{

//1.读取配置文件,生成字节输入流

in = Resources.getResourceAsStream(“SqlMapConfig.xml”);

//2.创建SqlSessionFactory工厂

SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

factory = builder.build(in);

}

@After // 用于在测试方法执行后执行

public void destory() throws Exception{

in.close();

}

/**

  • 测试二级缓存

*/

@Test

public void testSecondLevelCache(){

SqlSession sqlSession1 = factory.openSession();

IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);

User user1 = dao1.findById(41);

System.out.println(user1);

sqlSession1.close(); //一级缓存消失

SqlSession sqlSession2 = factory.openSession();

IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);

User user2 = dao2.findById(41);

System.out.println(user2);

sqlSession2.close();

System.out.println(user1==user2);

}

}

运行结果:

2021-02-16 17:07:05,474 179 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Opening JDBC Connection

2021-02-16 17:07:05,723 428 [ main] DEBUG source.pooled.PooledDataSource - Created connection 905735620.

2021-02-16 17:07:05,723 428 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]

2021-02-16 17:07:05,726 431 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Preparing: select * from user where id = ?

2021-02-16 17:07:05,752 457 [ main] DEBUG m.keafmd.dao.IUserDao.findById - ==> Parameters: 41(Integer)

2021-02-16 17:07:05,770 475 [ main] DEBUG m.keafmd.dao.IUserDao.findById - <== Total: 1

com.keafmd.domain.User@400cff1a

2021-02-16 17:07:05,777 482 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]

2021-02-16 17:07:05,778 483 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@35fc6dc4]

2021-02-16 17:07:05,778 483 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 905735620 to pool.

2021-02-16 17:07:05,782 487 [ main] DEBUG com.keafmd.dao.IUserDao - Cache Hit Ratio [com.keafmd.dao.IUserDao]: 0.5

com.keafmd.domain.User@75c072cb

false

Process finished with exit code 0

经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

注意:二级缓存中存放的内容是数据,而不是对象,内容虽然是同样的,但是存放的是数据,第二次虽然没有发起查询,但从二级缓存中获取时,会把内容封装到新的对象返回,所以是false。

二级缓存注意事项

当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

User:

package com.keafmd.domain;

import java.io.Serializable;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。**

注意:二级缓存中存放的内容是数据,而不是对象,内容虽然是同样的,但是存放的是数据,第二次虽然没有发起查询,但从二级缓存中获取时,会把内容封装到新的对象返回,所以是false。

二级缓存注意事项

当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

User:

package com.keafmd.domain;

import java.io.Serializable;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-VnDDlR5k-1715574659507)]

[外链图片转存中…(img-daypkstB-1715574659508)]

[外链图片转存中…(img-MmRBSIj4-1715574659508)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值