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. Java并发之synchronized关键字深度解析(三)

    前言 本篇主要介绍一下synchronized的批量重偏向和批量撤销机制,属于深水区,大家提前备好氧气瓶. 上一篇说完synchronized锁的膨胀过程,下面我们再延伸一下synchronized锁 ...

  2. pytho GUI编程之Tkinter

    摘录 python核心编程s GUI(Graphical User Interface)图形用户界面. Tcl.Tk和Tkinter Tkinter是python的默认GUI库.它基于Tk工具包,该工 ...

  3. Oracle 创建用户,赋予指定表名/视图只读权限

    步骤指南 创建用户 格式:; 语法:create user 用户名 identified by 密码; 注:密码不行的话,前后加(单引号):' create user TEST identified ...

  4. Could not find any version that matches com.android.support:appcompat-v7:29.+

    新学Android开发设计用到Android Studio,说实话真的是BUG满天飞,稍有不慎就会蹦出一个不明所以的问题. 新建Android工程时编译运行报错: 目测是本地文件和工程对应的依赖包不匹 ...

  5. ZKWeb 官网与演示站点的部署步骤 (Linux + Nginx + Certbot)

    因为没有给域名续费,加上私人时间不足,ZKWeb 的官网和演示站点已经停止了几个月的时间. 最近时间开始变多,所以重新购买了别的域名和服务器把官网和演示站点重新部署上去. 在此前站点是托管在共享主机上 ...

  6. Java并发面试问题,谈谈你对AQS的理解

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  7. js Set对象

    1.将数组转换成Set对象 let arr1 = new Set([1,2,3,4]) console.log(arr1) //{1,2,3,4} 2.数组去重 let arr2 = new Set( ...

  8. 过滤器(Filter)对登陆页面进行过滤验证

    import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServl ...

  9. digitalworld.local:Torment Vulnhub Walkthrough

    主机层面扫描: ╰─ nmap -p1-65535 -sV -A 10.10.202.135Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-09 ...

  10. arcgis api for javascript 学习(七) 调用发布地图信息,并将地图属性信息输出到Excel表格---进阶版

    我们在arcgis api for javascript 学习(三)已经学习到了关于调用地图信息进行属性输出的问题,不过通过代码我们实现后会发现还是有一些小瑕疵的,比如我们只能单个数据属性的输出,如果 ...