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中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 代表不开启二级缓存。
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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合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开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!