缓存(cache-面试会问)
什么是缓存:
缓存中存储的是数据,目的是让查询更快
缓存使用总结:
1 每一次查询都先从缓存查询
2 如果查到直接返回
3 如果缓存中没有查到该数据,就建立连接,查询数据库
4 查询完数据库,再放入缓存,再相应给前端
5 后续每一次查询,重复上面步骤
6 增删改都会清空一二级缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2AbdgSB-1663858128223)(day54_mybatis.assets/image-20220922111311472.png)]
缓存的好处:减少数据库查询次数,从而减少响应时间,减少对数据库的查询
mybatis的缓存
mybatis本身是支持缓存,而且提供两种缓存级别,一级缓存
和二级缓存
.默认开启
一级缓存.
一级缓存
演示如下: 同一个数据
查询两次,第二次直接从缓存拿数据,并没有发生SQL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pT0ccij-1663858128224)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922111021040.png)]
一级缓存是
SqlSession级别
.同一个SqlSession得到mapper中的数据会进入SqlSession级别的缓存.
同一个SqlSession演示,会命中缓存
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
UserMapper mapper2= sqlSession.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
}
不同SqlSession演示
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到1号sessio
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号session
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2= sqlSession2.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxDfsQHB-1663858128225)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922112015277.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7hu2lXMC-1663858128225)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922112204946.png)]
演示: 增删改后,会清空缓存
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession1.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
User user = new User( );
user.setId(29);
user.setUsername("腾飞");
user.setPassword("123456");
// 执行一次更新,会清空缓存
mapper1.updateUserById(user);
sqlSession1.commit();
System.out.println("-------------------" );
// 第二次查询,因为缓存被清空,要重新查数据库
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wkl4vRXH-1663858128226)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922112704296.png)]
思考题: 缓存是使用什么结构存储的? 存储的key是什么? value是什么?
二级缓存
二级缓存是Mapper级别,比SqlSession级别要大.通过同一个Mapper文件得到SqlSession都可以共享数据.但是需要手动开启
.
开启步骤:
1 mybatis-config.xml设置开启缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
2 Mapper.xml中指定缓存类型
<!-- 设置缓存的实现类 默认是PerpetualCache来实现缓存 ps: 此处可以指定其他类来实现缓存,例如EHCache --> <cache />
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到1号SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 得到3号SqlSession
SqlSession sqlSession3 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 关流时,才会将数据放入缓存
sqlSession1.close();
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
// 关流时,才会将数据放入缓存
sqlSession2.close();
System.out.println("-------------------" );
// 第三次查询
User user3 = mapper3.findUserById(29);
System.out.println(user3 );
// 关流时,才会将数据放入缓存
sqlSession3.close();
}
注意:二级缓存时要将实体类系列化否则报错
Cause: java.io.NotSerializableException
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqA5LFJF-1663858128226)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922151013626.png)]
演示: 增删改会清空缓存
@Test
public void hello() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到1号SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession( );
// 得到2号SqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession( );
// 得到3号SqlSession
SqlSession sqlSession3 = sqlSessionFactory.openSession( );
// 同一个SQLsession下的mapper,会命中缓存
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询
User user1 = mapper1.findUserById(29);
System.out.println(user1 );
System.out.println("-------------------" );
// 关流时,才会将数据放入缓存
sqlSession1.close();
System.out.println("-------------------" );
// 第二次查询
User user2 = mapper2.findUserById(29);
System.out.println(user2 );
// 执行更新
User user = new User( );
user.setId(29);
user.setUsername("腾飞");
user.setPassword("123456");
int i = mapper2.updateUserById2(user);
System.out.println(i );
sqlSession2.commit();
sqlSession2.close();
System.out.println("-------------------" );
// 第三次查询
User user3 = mapper3.findUserById(29);
System.out.println(user3 );
// 关流时,才会将数据放入缓存
sqlSession3.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z0FuFOQq-1663858128227)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922151546984.png)]
面试问:$和#的区别
使用#
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfRztIRH-1663858128228)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922155116760.png)]
使用$
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxRJ1io3-1663858128229)(D:/%E5%8D%83%E5%B3%B0%E4%B8%8A%E8%AF%BE/Every%20Day%20Stage3/day54/code/day54_mybatis.assets/image-20220922155245304.png)]
总结:
{} 相当于是预处理语句,会将#换成占位符,字符串等数据赋值时自动拼接引号,可以避免SQL注入
${} 相当于是处理语句,直接原样将数据取出,容易造成SQL注入
复习JDBCSQL注入部分
Log4j使用(了解)
1 使用log4j将日志输出到本地磁盘文件
1 log4j.properties文件中设置输出到本地的磁盘的模板
2 指定使用这套模板
log4j.logger.com.qf.mapper=debug,B
# 将日志输出到本地磁盘
log4j.appender.B = org.apache.log4j.DailyRollingFileAppender
log4j.appender.B.File = E://000_J_A_V_A//Java2212//第三阶段//day54//mybatis.log
log4j.appender.B.Append = true
log4j.appender.B.Threshold = DEBUG
log4j.appender.B.layout = org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss} method:%l%n%m%n
2 使用log4j将日志输出到mysql表[了解]
准备一张表存储日志,这张表包括但不限于日志级别,线程名,时间,方法名,日志信息等等
CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
设置properties文件,给定一个输出到mysql的模板
log4j.logger.com.qf.mapper=debug,logDB
### 输出到mysql
log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout=org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver=com.mysql.jdbc.Driver
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/java2212
log4j.appender.logDB.User=root
log4j.appender.logDB.Password=123456
log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('day54_mybatis','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
测试使用
3 自己的代码中使用log4j记录日志
以前是写输出语句调试代码,不灵活,只能输出到控制台,格式不固定等等都是毛病.
使用思路: 以前写sout的地方,现在就使用log4j记录
package com.qf;
import org.apache.log4j.Logger;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class UserServiceImpl {
// 1 获得日志对象
private static Logger logger = Logger.getLogger(UserServiceImpl.class);
public void testLog(){
// 2使用日志对象记录日志
logger.debug("DEBUG-查询返回对象User");
logger.info("info-查询返回对象User");
logger.warn("warn-查询返回对象User");
try {
}catch (Exception e){
logger.error(e.getMessage());
}
}
}
gger(UserServiceImpl.class);
public void testLog(){
// 2使用日志对象记录日志
logger.debug("DEBUG-查询返回对象User");
logger.info("info-查询返回对象User");
logger.warn("warn-查询返回对象User");
try {
}catch (Exception e){
logger.error(e.getMessage());
}
}
}