Mybatis延迟加载和查询缓存

Mybatis延迟加载和查询缓存Mybatis延迟加载和查询缓存

大家好,又见面了,我是你们的朋友全栈君。

一、延迟加载

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
  延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading

设置项 描述 允许值 默认值
lazyLoadingEnabled 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 true \ false false
aggressiveLazyLoading 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 true \ false true
//例如
<settings>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="aggressiveLazyLoading" value="false"/>
</settings>

场合:
当只有部分记录需要关联查询其它信息时,此时可按需延迟加载,需要关联查询时再向数据库发出sql,以提高数据库性能。
当全部需要关联查询信息时,此时不用延迟加载,直接将关联查询信息全部返回即可,可使用resultType或resultMap完成映射。

二、查询缓存

Mybatis提供查询缓存,用于减轻数据压力,提高数据库压力。
  Mybatis提供一级缓存和二级缓存。
   在这里插入图片描述
  在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于缓存数据。
  不同的SqlSession之间的缓存数据区域是互相不影响的。
  Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
  Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。

1、一级缓存

在这里插入图片描述
  第一次发起查询,先去找缓存中是否有id为1的用户信息,如果没有,从数据库中查询用户信息。
  得到用户信息,将用户信息存储到一级缓存中。
  第二次发起查询用户id为1的用户信息,先去缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
  如果SqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存。目的是为了让缓存中存储的是最新的信息,避免脏读。
  Mybatis默认支持一级缓存,不需要在配置文件中配置。
  Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象。
  应用场景:
  在这里插入图片描述

2、二级缓存

在这里插入图片描述
  SqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
  SqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
  二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分。
每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。
  Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
  sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。
  开启二级缓存:
  在核心配置文件SqlMapConfig.xml中加入

    <setting name="cacheEnabled" value="true"/>
描述 允许值 默认值
cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置。 true false true

要在你的Mapper映射文件中添加一行: ,表示此mapper

开启二级缓存。

二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。
  为了将缓存数据取出执行反序列化,因为二级缓存存储介质多种多样,不一定在内存。

禁用二级缓存:

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

刷新缓存(就是清空缓存):
  设置statement配置中的flushCache=“true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

应用场景:
  对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
  局限性:
  mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

案例

在这里插入图片描述
UserMapper.java

package com.xbj.mapper;
import com.xbj.po.TUser;
import java.util.List;
/** * 延迟加载 */
public interface UserMapper { 
   
    //延迟加载
    List<TUser> userIncludeOrder();
    TUser findUserById(Integer id);
    List<TUser> findAll();
    void deleteById(Integer id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xbj.mapper.UserMapper">
<!--开启二级缓存-->
<cache />
<!--SQL片段的引用-->
<sql id="baseSql">
SELECT * from t_user
</sql>
<!--延迟加载-->
<resultMap id="userInfos" type="TUser" autoMapping="true">
<id property="id" column="id"/>
<collection property="tOrders" ofType="TOrder" select="findorderByUid" column="id"/>
</resultMap>
<select id="findorderByUid" resultType="TOrder" parameterType="int">
SELECT  * from t_order where uid=#{id}
</select>
<select id="userIncludeOrder" resultMap="userInfos">
SELECT * from t_user
</select>
<select id="findUserById" parameterType="int" resultType="TUser">
SELECT * FROM t_user where id=#{id}
</select>
<select id="findAll" resultType="TUser">
SELECT * from t_user
</select>
<delete id="deleteById" parameterType="int" >
DELETE  from t_user WHERE id=#{id}
</delete>
</mapper>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>dome02</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
</project>

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>
<settings>
<!--允许延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.xbj.po" />
</typeAliases>
<!--数据源的配置-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--对象的操作-->
<mappers>
<!--<mapper resource="com/com.xbj/mapper/UserMapper.xml"/>-->
<!--<mapper url="file:///D:\projects\javaprojects\620\Demo02\src\main\resources\com\com.xbj\mapper\UserMapper.xml" />-->
<!--<mapper class="com.com.xbj.mapper.UserMapper" />-->
<package name="com.xbj.mapper" />
</mappers>
</configuration>

log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

TOrder.java

package com.xbj.po;
import java.io.Serializable;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class TUser implements Serializable {
private int id;
private String username;
private String password;
private Integer age;
private List<TOrder> tOrders;
public List<TOrder> gettOrders() {
return tOrders;
}
public void settOrders(List<TOrder> tOrders) {
this.tOrders = tOrders;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TUser tUser = (TUser) o;
if (id != tUser.id) return false;
if (username != null ? !username.equals(tUser.username) : tUser.username != null) return false;
if (password != null ? !password.equals(tUser.password) : tUser.password != null) return false;
if (age != null ? !age.equals(tUser.age) : tUser.age != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "TUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}

TUser.java

package com.xbj.po;
import java.io.Serializable;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class TUser implements Serializable {
private int id;
private String username;
private String password;
private Integer age;
private List<TOrder> tOrders;
public List<TOrder> gettOrders() {
return tOrders;
}
public void settOrders(List<TOrder> tOrders) {
this.tOrders = tOrders;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TUser tUser = (TUser) o;
if (id != tUser.id) return false;
if (username != null ? !username.equals(tUser.username) : tUser.username != null) return false;
if (password != null ? !password.equals(tUser.password) : tUser.password != null) return false;
if (age != null ? !age.equals(tUser.age) : tUser.age != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "TUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}

test.java

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.xbj.mapper.UserMapper;
import com.xbj.po.TUser;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class test {
private UserMapper userMapper;
@Before
public void init() throws IOException {
InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
userMapper=new SqlSessionFactoryBuilder().build(is).openSession(true).getMapper(UserMapper.class);
}
//测试延迟加载
@Test
public void t1(){
List<TUser> infos=userMapper.userIncludeOrder();
System.out.println(infos);
}
//测试一级缓存
@Test
public void t2(){
TUser user1=userMapper.findUserById(1);
TUser user2=userMapper.findUserById(1);
}
/*在进行增、改、删操作的时候会清空缓存*/
@Test
public void t3(){
userMapper.findAll();
userMapper.deleteById(4);
userMapper.findAll();
}
}

Test02.java

import com.xbj.mapper.UserMapper;
import com.xbj.po.TUser;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class Test02 {
private SqlSessionFactory factory;
private UserMapper userMapper;
private UserMapper userMapper2;
@Before
public void init() throws IOException {
InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
factory=new SqlSessionFactoryBuilder().build(is);
userMapper=factory.openSession(true).getMapper(UserMapper.class);
userMapper2=factory.openSession(true).getMapper(UserMapper.class);
}
/*测试在不同的mapper会话中,是访问不同的缓存区*/
@Test
public void t1(){
TUser user1=userMapper.findUserById(1);
TUser user2=userMapper2.findUserById(1);
}
/*在进行增、改、删操作的时候会清空缓存*/
@Test
public void t2(){
userMapper.findAll();
userMapper.deleteById(4);
userMapper.findAll();
}
/*测试二级缓存的时候,需要将第一个查询操作先关闭,否则程序不会进入缓存里面查找数据*/
@Test
public void t3(){
SqlSession sqlSession1=factory.openSession(true);
SqlSession sqlSession2=factory.openSession(true);
sqlSession1.selectOne("findUserById",1);
sqlSession1.close();  //第一个人 调用后 会话结束 写入缓存
sqlSession2.selectOne("findUserById",1);
}
}

sql.sql

/*
Navicat MySQL Data Transfer
Source Server         : Mysql
Source Server Version : 50717
Source Host           : localhost:3306
Source Database       : user
Target Server Type    : MYSQL
Target Server Version : 50717
File Encoding         : 65001
Date: 2019-06-21 15:06:09
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_item
-- ----------------------------
DROP TABLE IF EXISTS `t_item`;
CREATE TABLE `t_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`detail` varchar(200) DEFAULT NULL,
`price` float(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_item
-- ----------------------------
INSERT INTO `t_item` VALUES ('1', '苹果', '真好吃', '6.00');
INSERT INTO `t_item` VALUES ('2', '橘子', '淮南橘子', '3.00');
INSERT INTO `t_item` VALUES ('3', '香蕉', '海南香蕉,香蕉中的战斗蕉', '2.00');
INSERT INTO `t_item` VALUES ('4', '芒果', '泰国芒果', '11.00');
-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `t_order_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_order
-- ----------------------------
INSERT INTO `t_order` VALUES ('1', '1');
INSERT INTO `t_order` VALUES ('2', '1');
INSERT INTO `t_order` VALUES ('3', '1');
INSERT INTO `t_order` VALUES ('4', '2');
INSERT INTO `t_order` VALUES ('5', '3');
-- ----------------------------
-- Table structure for t_order_detail
-- ----------------------------
DROP TABLE IF EXISTS `t_order_detail`;
CREATE TABLE `t_order_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`oid` int(11) DEFAULT NULL,
`item_id` int(11) DEFAULT NULL,
`count` float(8,2) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `oid` (`oid`),
KEY `item_id` (`item_id`),
CONSTRAINT `t_order_detail_ibfk_1` FOREIGN KEY (`oid`) REFERENCES `t_order` (`id`),
CONSTRAINT `t_order_detail_ibfk_2` FOREIGN KEY (`item_id`) REFERENCES `t_item` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_order_detail
-- ----------------------------
INSERT INTO `t_order_detail` VALUES ('1', '1', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('2', '1', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('3', '2', '3', '2.00');
INSERT INTO `t_order_detail` VALUES ('4', '2', '4', '2.00');
INSERT INTO `t_order_detail` VALUES ('5', '3', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('6', '3', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('7', '4', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('8', '4', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('9', '5', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('10', '1', '4', '2.00');
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(200) NOT NULL,
`password` varchar(200) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', 'tom', 'ok', '33');
INSERT INTO `t_user` VALUES ('2', 'lucy', 'ok', '43');
INSERT INTO `t_user` VALUES ('3', 'jack', 'ok', '53');
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106619.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 一:计算机基础入门及介绍[通俗易懂]

    一:计算机基础入门及介绍[通俗易懂]一:计算机基础入门篇1.计算机系统计算机(Conputer):俗称电脑,一种能接收和存储信息,并按照存储在其内部的程序对海量数据进行自动、高速的处理,然后把处理结果输出的现代化智能电子设备。…

    2022年10月21日
  • linux系统退出vim

    linux系统退出vim在linux家族中,vim编辑器是系统自带的文本编辑器,其功能强大自不必说了。进入VI编辑器后,无法退出以致强抽关机,确是不得以呀。进入VIM编辑器1 VIM编辑器,可以新建文件也可以修改文件,命令为:vim /usr/local/con.cfg如图!2 如果这个文件,以前是没有的,则为新建,则下方有提示为新文件,如图。如果文件已存在,则没有提示。3 进入编辑器后,我们先按"I”,即切换到“插入…

  • db2中You can’t specify target table for update in FROM clause错误

    db2中You can’t specify target table for update in FROM clause错误db2中You can’t specify target table for update in FROM clause错误

  • Intel参数_intel g系列

    Intel参数_intel g系列http://www.intel.com/support/cn/mt/mt_win.htm

    2022年10月29日
  • 通达OA 公共文件柜二次开发添加管理信息(图文)[通俗易懂]

    通达OA 公共文件柜二次开发添加管理信息(图文)

  • 搭建一个QQ机器人叫女友起床

    搭建一个QQ机器人叫女友起床目录前言具体实现1、定时发送信息2、让机器人陪女朋友聊天3、调用一些有趣的接口健康填报小助手开发中。。。写在最后前言上一篇文章介绍了怎么配置机器人框架,并且实现了一些简单的功能。(发送私聊或者群聊信息、接收上报的事件、简单的自动回复等等)这次为了让QQ机器人更加智能,调用了一些实用的接口。通过自己搭建的机器人实现定时叫女朋友起床、和女朋友聊天等功能。如上图所示,我的机器人每天都会准时叫女朋友起床;并且在我忙的时候然而女朋友无聊的时候可以陪她聊一会天。具体实现以下实现的功能都需要机器人已经配置完

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号