SSM框架之Mybatis(7)延迟加载、缓存及注解
Mybatis(7)延迟加载、缓存及注解
1、延迟加载
延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
**好处:**先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速
度要快。
坏处 :
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗
时间,所以可能造成用户等待时间变长,造成用户体验下降
2、Mybatis缓存
像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提
高性能。
Mybatis 中缓存分为一级缓存,二级缓存。
2.1、一级缓存
一级缓存是SqlSession级别的缓存,只要SqlSession没有flush、close或clearCache,那么缓存就会存在。
2.1.1、测试一级缓存的存在
测试方法
@Test
public void testFindOne(){
//通过id查找用户
User user1 = uesrdao.findById(48);
System.out.println(user1.hashCode());
//sqlSession.clearCache();
User user2 = uesrdao.findById(48);
System.out.println(user2.hashCode());
System.out.println(user1 == user2);
}
运行结果:
Opening JDBC Connection
Created connection 278934944.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@10a035a0]
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
< Total: 1
1832580921
1832580921
true
分析
上面的结果显示虽然我们进行了两次查询,但是只执行了一次数据库操作。这就是一级缓存在起作用,因为一级缓存的存在所以在第二次查询id = 48时,没有进行数据库的操作,而是直接从一级缓存中读取数据。
2.1.2、一级缓存
一级缓存是SqlSession级别的缓存,当调用SqlSession缓存的 添加,更新、删除、commit()、close()等操作时,会清空一级缓存。
第一次sqlsession对象执行查询操作id = 48时,先去缓存中查询是否存在有id=48的用户信息,没有就执行sql语句,然后从数据库中查询数据。
得到用户信息后就把用户信息储存在一级缓存中。
准确来说,存入一级缓存的是由查询语句返回的对象,而不是具体的数据
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存
中获取用户信息。
如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样
做的目的为了让缓存中存储的是最新的信息,避免脏读。
2.1.3、对清除缓存的操作
@Test
public void testFindOne(){
//通过id查找用户
User user1 = uesrdao.findById(48);
System.out.println(user1.hashCode());
sqlSession.clearCache();
User user2 = uesrdao.findById(48);
System.out.println(user2.hashCode());
System.out.println(user1 == user2);
}
执行结果:
Opening JDBC Connection
Created connection 278934944.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@10a035a0]
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
< Total: 1
1832580921
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
< Total: 1
369241501
false
清空缓存后查询数据库的信息,查询了两次。
2.2、二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个
SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
及可以认为二级缓存是依赖于SqlSessionFactory,SqlSessionFactory对象产生时,就存在,并且由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)在configuration标签中开启setting标签
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
setting标签
| 设置名 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
| cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
false 代表不开启二级缓存。
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
在mapper标签中写
<mapper namespace="dao.IUserDao">
<cache/>
</mapper>
cache标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
第三步:让当前的操作支持二级缓存(在select标签中配置)
在mapper标签中写
<mapper namespace="dao.IUserDao">
<cache/>
<select id="findById" parameterType="int" resultType="domain.User" useCache="true">
select * from user where id=#{id}
</select>
</mapper>
将 UserDao.xml 映射文件中的select标签中设置 useCache=”true”代表当前这个 statement 要使用
二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。
第四步:添加测试类
package test;
import dao.IUserDao;
import domain.QueryVo;
import 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.Date;
import java.util.List;
public class CacheTest {
/**
* 测试mybatis的CRUD操作
*/
InputStream in;
SqlSessionFactory build;
@Before
public void init() throws Exception {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
build = new SqlSessionFactoryBuilder().build(in);
}
@After
public void destory() throws Exception {
in.close();
}
@Test
public void testFindOne(){
SqlSession sqlSession1 = build.openSession();
IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
sqlSession1.close();//一级缓存消失
SqlSession sqlSession2 = build.openSession();
IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = dao2.findById(41);
System.out.println(user2);
sqlSession2.close();
System.out.println(user1 == user2);
}
}
执行结果:
Opening JDBC Connection
Created connection 929776179.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
==> Preparing: select * from user where id=?
> Parameters: 41(Integer)
< Total: 1
User{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
Returned connection 929776179 to pool.
Cache Hit Ratio [dao.IUserDao]: 0.5
User{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}
false
经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
然而两次的User对象却不相同,这是因为二级缓存存储的是对应的具体数据
{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}
等到再次查询时才会new新的对象。
2.2.1、二级缓存的注意事项
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化
方式来保存对象。
/**
*
* <p>Title: User</p>
* <p>Description: 用户的实体类</p>
*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
3、Mybatis关于注解的开发
3.1、mybatis常用注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用
3.2、用注解实现CRUD操作
3.2.1、编写实体类
E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\java\domain\User.java
package domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
3.2.2、编写主配置文件
E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\resources\SqlMapConfig.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">
<configuration>
<!--引入外部配置文件-->
<properties resource="jdbcConfig.properties"/>
<!--配置别名-->
<typeAliases>
<package name="domain"/>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--配置dao接口所在位置-->
<mappers>
<package name="dao"/>
</mappers>
</configuration>
3.2.3、编写持久化接口
E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\java\dao\IUserDao.java
package dao;
import domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* 在mybatis中针对注解有四个注解
*@Select()、@Insert()、@Update()、@Delete()
*/
@CacheNamespace(blocking = true)
public interface IUserDao {
/**
* 查询所有
* @return
*/
@Select(value = "select * from user")
List<User> findAll();
/**
* 添加用户
* @param user
*/
@Insert("insert into user (username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
void addUser(User user);
/**
* 根据id删除指定用户
* @param id
*/
@Delete("delete from user where id = #{id}")
void deleteUser(int id);
/**
* 更新用户数据
* @param user
*/
@Update("update user set username = #{username},address = #{address},sex = #{sex},birthday = #{birthday} where id = #{id}")
void updateUser(User user);
@Select("select * from user where id = #{id}")
User findById(int id);
/**
* 模糊查询
* @param name
* @return
*/
//@Select("select * from user where username like #{str}")
@Select("select * from user where username like '%${value}%'")
List<User> findByName(String name);
/**
*
* @return
*/
@Select("select count(id) from user")
int findTotal();
}
3.2.4、编写相应测试类
package test;
import dao.IUserDao;
import 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.Date;
import java.util.List;
public class AnnotationTest {
InputStream in;
SqlSessionFactory factory;
SqlSession sqlSession;
IUserDao userDao;
@Before
public void init() throws Exception {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void destory() throws Exception {
sqlSession.commit();
sqlSession.close();
in.close();
}
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user:users){
System.out.println(user);
}
}
@Test
public void testAddUser(){
User user = new User();
user.setUsername("wf");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("中国");
userDao.addUser(user);
}
@Test
public void testDeleteUser(){
userDao.deleteUser(49);
}
@Test
public void testUpdateUser(){
User user = new User();
user.setId(50);
user.setUsername("gx");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("中国");
userDao.updateUser(user);
}
@Test
public void testFindById(){
User user = userDao.findById(50);
System.out.println(user);
}
@Test
public void testFindByName(){
//List<User> users = userDao.findByName("%王%");
List<User> users = userDao.findByName("王");
for (User user:users){
System.out.println(user);
}
}
@Test
public void testFindTotal(){
int a = userDao.findTotal();
System.out.println(a);
}
}
3.4、mybatis基于注解的二级缓存
3.4.1、在SqlMapConfig中开启二级缓存支持
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
3.4.2、在持久层接口中使用注解配置二级缓存
/**
*
* <p>Title: IUserDao</p>
* <p>Description: 用户的持久层接口</p>
*/
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}
SSM框架之Mybatis(7)延迟加载、缓存及注解的更多相关文章
- SSM框架-初学Mybatis框架
SSM(Spring+SpringMVC+Mybatis)是目前项目开发比较流行的一套组合框架,而Mybatis是负责数据库操作的那部分框架,具体 我也说不上来 传统的JDBC操作比较冗长而繁琐,而用 ...
- SSM框架之MyBatis框架实现简单的增删改查
MyBatis框架介绍 MyBatis是一个优秀的数据持久层框架,在实体类和SQL语句之间建立映射关系是一种半自动化的ORM实现,其封装性要低于Hibernate,性能优越,并且小巧,简单易学,应用也 ...
- 【mybatis annotation】数据层框架应用--Mybatis(二) 基于注解实现数据的CRUD
使用MyBatis框架进行持久层开发 MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架. MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索. MyBa ...
- SSM框架-使用MyBatis Generator自动创建代码
参考:http://blog.csdn.net/zhshulin/article/details/23912615 SSM搭建的时候用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半 ...
- SSM框架——使用MyBatis Generator自动创建代码
版权声明:本文为博主原创文章,未经博主允许不得转载. 这两天需要用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半自动的ORM框架,所以主要的工作就是配置Mapping映射文件,但是 ...
- 从0开始整合SSM框架-1.mybatis
1.建立maven项目 2.首先引入mybatis需要引入的依赖(1).数据库驱动(2).mybatis核心包 <!-- mysql数据库驱动--> <!-- https://mvn ...
- 转:SSM框架——使用MyBatis Generator自动创建代码
转:https://blog.csdn.net/zhshulin/article/details/23912615 这两天需要用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半自动的 ...
- SSM框架之Mybatis(1)入门
Mybatis(1)入门 1.mybatis的概述 mybatis是一个持久层框架,用java编写的. 它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等 ...
- SSM框架之MyBatis入门介绍
一.什么是MyBatis? MyBatis源自Apache的iBatis开源项目, 从iBatis3.x开始正式更名为MyBatis.它是一个优秀的持久层框架. 二.为什么使用MyBatis? 为了和 ...
随机推荐
- 一个proc预编译代码时coredump的问题分析
最近有同事在搞编译环境迁移,碰上一个问题让我帮他看一下. 他建了一个新目录,然后把现在的代码拷过去,编译的时候发现有一个文件编译不了一执行就出现core,不知道啥情况. 我进到他的编译环境 ...
- RMAN备份数据库与表空间
脚本: 数据库备份: backup database format='/u01/app/oracle/oradata/Backup/oradb_%d_%s.bak'; 表空间备份:backup tab ...
- docker 使用mysqldump命令备份导出项目中的mysql数据
下图为镜像重命名后的镜像名为uoj,现在要把这个镜像中的mysql导出 运行如下命令: docker exec -it uoj mysqldump -uroot -proot app_uoj233 & ...
- 1.Redux学习1,Redux
Redux流程图如上: Action就是一条命令 Store顾名思义就是存储数据的, Reducers是一个回调函数用于处理数据,它处理完数据会返回给Store存储起来 基本流程就是:组件中用Stor ...
- H5和ionic RN和Weex 他们之间的比较
使用H5和ionic 进行移动APp开发 首先要开发一个完整的网站,然后,在网站的基础上,使用 H5+huozhe ionic提供的打包技术,把网站打包成一个应用. 把网站打包成一个可以安装到手机上运 ...
- 全文检索--Lucene & ElasticSearch
全文检索--Lucene 2.1 全文检索和以前高级查询的比较 1.高级查询 缺点:1.like让数据库索引失效 2.每次查询都是查询数据库 ,如果访问的人比较多,压力也是比较大 2.全文检索框架:A ...
- React-Native三种断点调试方式的流程和优缺点比较
RN的调试和web端的调试虽然相似,但是也有一些不同,下面就来比较一下三种断点调试方法的差异 总结: 感觉还是第一种好一些 1.React-Native-Debugger工具调试法 1.1 首先我们得 ...
- Java 9 ← 2017,2019 → Java 13,来看看Java两年来的变化
距离 2019 年结束,只剩下 33 天了.你做好准备迎接 2020 年了吗? 一到年底,人就特别容易陷入回忆和比较之中,比如说这几天的对比挑战就火了! 这个话题登上了微博的热搜榜,也刷爆了朋友圈, ...
- Azure上的时序见解,Time series insights
5G来了,广连接(mmTC)可以实现每平方千米100万的连接数(理论值),是4G的10倍,5G网络出现,配合其他技术,空间将在数据意义上剧烈压缩,车联网.智能家居.智能安防.智慧工厂.智慧能源都可能带 ...
- 从零开始的vue学习笔记(一)
前言 项目要用vue.js,今天开始自学vue.js官方教程,记录下自己的学习摘要,方便后面查阅(此笔记按照学习天数,每天一篇) Vue.js是什么 Vue是一套用于构建用户界面的渐进式框架,Vue ...