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)延迟加载、缓存及注解的更多相关文章

  1. SSM框架-初学Mybatis框架

    SSM(Spring+SpringMVC+Mybatis)是目前项目开发比较流行的一套组合框架,而Mybatis是负责数据库操作的那部分框架,具体 我也说不上来 传统的JDBC操作比较冗长而繁琐,而用 ...

  2. SSM框架之MyBatis框架实现简单的增删改查

    MyBatis框架介绍 MyBatis是一个优秀的数据持久层框架,在实体类和SQL语句之间建立映射关系是一种半自动化的ORM实现,其封装性要低于Hibernate,性能优越,并且小巧,简单易学,应用也 ...

  3. 【mybatis annotation】数据层框架应用--Mybatis(二) 基于注解实现数据的CRUD

    使用MyBatis框架进行持久层开发 MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架. MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索. MyBa ...

  4. SSM框架-使用MyBatis Generator自动创建代码

    参考:http://blog.csdn.net/zhshulin/article/details/23912615 SSM搭建的时候用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半 ...

  5. SSM框架——使用MyBatis Generator自动创建代码

    版权声明:本文为博主原创文章,未经博主允许不得转载. 这两天需要用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半自动的ORM框架,所以主要的工作就是配置Mapping映射文件,但是 ...

  6. 从0开始整合SSM框架-1.mybatis

    1.建立maven项目 2.首先引入mybatis需要引入的依赖(1).数据库驱动(2).mybatis核心包 <!-- mysql数据库驱动--> <!-- https://mvn ...

  7. 转:SSM框架——使用MyBatis Generator自动创建代码

    转:https://blog.csdn.net/zhshulin/article/details/23912615 这两天需要用到MyBatis的代码自动生成的功能,由于MyBatis属于一种半自动的 ...

  8. SSM框架之Mybatis(1)入门

    Mybatis(1)入门 1.mybatis的概述 mybatis是一个持久层框架,用java编写的. 它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等 ...

  9. SSM框架之MyBatis入门介绍

    一.什么是MyBatis? MyBatis源自Apache的iBatis开源项目, 从iBatis3.x开始正式更名为MyBatis.它是一个优秀的持久层框架. 二.为什么使用MyBatis? 为了和 ...

随机推荐

  1. SpringBoot微服务电商项目开发实战 --- api接口安全算法、AOP切面及防SQL注入实现

    上一篇主要讲了整个项目的子模块及第三方依赖的版本号统一管理维护,数据库对接及缓存(Redis)接入,今天我来说说过滤器配置及拦截设置.接口安全处理.AOP切面实现等.作为电商项目,不仅要求考虑高并发带 ...

  2. 分享一个web安全学习站点

    大神建议: https://blog.knownsec.com/Knownsec_RD_Checklist/v3.0.html#FMID_1218170279FM https://websec.rea ...

  3. 研究STM32F4的IEEE1558 PTP网络时间同步协议实现,软件是RL-TCPnet V7.X的底层

    这个东西发现挺有意思,刚开始研究没整明白怎么用,实测设置一次时间戳就可以使用了,后面在深入研究下 extern ARM_DRIVER_ETH_MAC Driver_ETH_MAC0; ARM_ETH_ ...

  4. 一泡尿的时间,快速读懂QUIC协议

    1.TCP协议到底怎么了? 现时的互联网应用中,Web平台(准确地说是基于HTTP及其延伸协议的客户端/服务器应用)的数据传输都基于 TCP 协议. 但TCP 协议在创建连接之前需要进行三次握手(如下 ...

  5. Spring Boot Security 保护你的程序

    Spring Boot Security 本示例要内容 基于角色的权限访问控制 加密.解密 基于Spring Boot Security 权限管理框架保护应用程序 String Security介绍 ...

  6. Koa 本地搭建 HTTPS 环境

    openssl 首先本地需要安装 openssl,用于生成自签名证书. $ brew install openssl 检查安装: $ openssl version LibreSSL 2.6.5 生成 ...

  7. C#通过反射调用类及方法

    反射有个典型的应用,就是菜单的动态加载,原理就是通过反射调用某个窗体(类).下面演示一下通过反射调用类及方法: 1.新建一个类,命名为:ReflectionHelper,代码如下: #region 创 ...

  8. 使用VeeValidate的data-vv-scope指定验证范围

    <div class="login" v-show="activeTab === 1"> <div class="panel-con ...

  9. django中使用原生的sql查询实例

    在app文件夹下创建database_operations.py文件,写如下内容: import pymysql from 项目名.settings import DATABASES class Da ...

  10. Docker容器镜像打成tar包

    简述需求: 在现在容器上保存镜像进行打包,在另一台服务上使用 首先查看下现有要打tar包的容器 docker ps -a 接下来用commit参数进行保存镜像, -a 提交人的姓名  -m “提交内容 ...