多次缓存和懒加载你都懂了吗?

举报
XiaoLin_Java 发表于 2022/03/27 20:49:35 2022/03/27
【摘要】 一、一级缓存 1.1、什么是缓存程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能 1.2 、Mybatis一级缓存简介:一级缓存的作用域是SQLSession,同一个SqlSession中执行相同的SQL查询(相同的SQL和参数),第一次会去查询数据库并写在缓存中,第二次会直接从缓存中取,且在mybatis中式默认开启默认开启...

一、一级缓存

1.1、什么是缓存

程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能

1.2 、Mybatis一级缓存

  • 简介:一级缓存的作用域是SQLSession,同一个SqlSession中执行相同的SQL查询(相同的SQL和参数),第一次会去查询数据库并写在缓存中,第二次会直接从缓存中取,且在mybatis中式默认开启默认开启一级缓存
  • 实现方式:基于PerpetualCache 的 HashMap本地缓存
  • 失效策略:当执行SQL时候两次查询中间发生了增删改的操作,即insert、update、delete等操作commit后会清空该SQLSession缓存; 比如sqlsession关闭,或者清空等

二、二级缓存

2.1、Mybatis二级缓存

二级缓存是namespace级别的,多个SqlSession去操作同一个namespace下的Mapper的sql语句,多个SqlSession可以共用二级缓存,如果两个mapper的namespace相同,(即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中,但是最后是每个Mapper单独的名称空间),默认是没有开启二级缓存,实现二级缓存的时候,MyBatis建议返回的POJO是可序列化的, 也就是建议实现Serializable接口

2.2、实现方式

基于PerpetualCache 的 HashMap本地缓存,可自定义存储源,如 Ehcache/Redis等

2.3、操作流程

第一次调用某个namespace下的SQL去查询信息,查询到的信息会存放该mapper对应的二级缓存区域。 第二次调用同个namespace下的mapper映射文件中,相同的sql去查询信息,会去对应的二级缓存内取结果

2.4、失效策略

执行同个namespace下的mapepr映射文件中增删改sql,并执行了commit操作,会清空该二级缓存

2.5、淘汰策略

会使用默认的*LRU 算法来收回(最近最少使用的)

2.6、开启缓存

mapper.xml

<!--开启mapper的namespace下的二级缓存-->
    <!--
        eviction:代表的是缓存回收策略,常见下面两种。
        (1) LRU,最近最少使用的,一处最长时间不用的对象
        (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们

        flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果不配置它,当SQL被执行的时候才会去刷新缓存。

        size:引用数目,代表缓存最多可以存储多少个对象,设置过大会导致内存溢出

        readOnly:只读,缓存数据只能读取而不能修改,默认值是false
    -->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

mapper全局配置:

<settings>
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存,全局总开关,这里关闭,mapper中开启了也没用-->
        <setting name="cacheEnabled" value="true" />
</settings>

注意

如果需要控制全局mapper里面某个方法不使用缓存,可以配置 useCache=“false”

<select id="selectById" parameterType="java.lang.Integer" resultType="Video" useCache="false">

select <include refid="base_video_field"/>  from video where id = #{video_id,jdbcType=INTEGER}

</select>

一级缓存和二级缓存使用顺序

优先查询二级缓存 —> 查询一级缓存 —> 数据库

三、懒加载

3.1、简介

按需加载,先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,并不是所有场景下使用懒加载都能提高效率

3.2、Mybatis懒加载

resultMap里面的association、collection有延迟加载功能

全局参数配置

<!--全局参数设置-->
<settings>
    <!--延迟加载总开关-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--将aggressiveLazyLoading设置为false表示按需加载,默认为true-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

mapper.xml

<resultMap id="VideoOrderResultMapLazy" type="VideoOrder">
        <id column="id" property="id"/>

        <result column="user_id" property="userId"/>
        <result column="out_trade_no" property="outTradeNo"/>
        <result column="create_time" property="createTime"/>
        <result column="state" property="state"/>
        <result column="total_fee" property="totalFee"/>
        <result column="video_id" property="videoId"/>
        <result column="video_title" property="videoTitle"/>
        <result column="video_img" property="videoImg"/>

<!-- 
select: 指定延迟加载需要执行的statement id 
column: 和select查询关联的字段
-->
        <association property="user" javaType="User" column="user_id" select="findUserByUserId"/>


</resultMap>

    <!--一对一管理查询订单, 订单内部包含用户属性  懒加载-->
<select id="queryVideoOrderListLazy" resultMap="VideoOrderResultMapLazy">

        select
         o.id id,
         o.user_id ,
         o.out_trade_no,
         o.create_time,
         o.state,
         o.total_fee,
         o.video_id,
         o.video_title,
         o.video_img
         from video_order o

</select>


<select id="findUserByUserId" resultType="User">

       select  * from user where id=#{id}

</select>

测试类

注意:dubug模式测试懒加载不准确,可以直接run

// resultmap association关联查询(测试懒加载)
VideoOrderMapper videoOrderMapper = sqlSession.getMapper(VideoOrderMapper.class);
List<VideoOrder> videoOrderList = videoOrderMapper.queryVideoOrderListLazy();
System.out.println(videoOrderList.size());

//6条订单记录,但是只查询3次用户信息,是因为部分用户信息走了一级缓存sqlsession
for(VideoOrder videoOrder : videoOrderList){

    System.out.println(videoOrder.getVideoTitle());
    System.out.println(videoOrder.getUser().getName());
}

四、代码

4.1、POJO类

User

package com.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

import java.util.Date;
import java.util.List;

/**
 * `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 *   `name` varchar(128) DEFAULT NULL COMMENT '昵称',
 *   `pwd` varchar(124) DEFAULT NULL COMMENT '密码',
 *   `head_img` varchar(524) DEFAULT NULL COMMENT '头像',
 *   `phone` varchar(64) DEFAULT '' COMMENT '手机号',
 *   `create_time` datetime DEFAULT NULL COMMENT '创建时间',
 */

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {

    private int id;

    private String name;

    private String pwd;

    private String headImg;

    private String phone;

    private Date createTime;

    private List<VideoOrder> videoOrderList;


}

Video

package com.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 *
 *
 * 视频类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Video {

    /**
     * 主键
     */
    private int id;

    /**
     * 视频标题
     */
    private String title;

    /**
     * 视频详情
     */
    private String summary;


    /**
     *视频封面图
     */
    private String coverImg;


    /**
     * 价格
     */
    private int price;


    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 评分
     */
    private Double point;



}

VideoOrder

package com.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

/**
 *  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 *   `out_trade_no` varchar(64) DEFAULT NULL COMMENT '订单唯一标识',
 *   `state` int(11) DEFAULT NULL COMMENT '0表示未支付,1表示已支付',
 *   `create_time` datetime DEFAULT NULL COMMENT '订单生成时间',
 *   `total_fee` int(11) DEFAULT NULL COMMENT '支付金额,单位分',
 *   `video_id` int(11) DEFAULT NULL COMMENT '视频主键',
 *   `video_title` varchar(256) DEFAULT NULL COMMENT '视频标题',
 *   `video_img` varchar(256) DEFAULT NULL COMMENT '视频图片',
 *   `user_id` int(12) DEFAULT NULL COMMENT '用户id',
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class VideoOrder {


    private int id;

    private String outTradeNo;

    private int  state;

    private Date createTime;

    private int  totalFee;


    private int videoId ;

    private String videoTitle;


    private String videoImg;


    private int userId;

    private User user;



}

4.2、Mapper接口

package com.mapper;

import com.pojo.User;
import com.pojo.VideoOrder;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface VideoOrderMapper {
    /***
     * 查询所有订单,关联用户信息
     * */
    List<VideoOrder> queryViddeoOrderList();

    List<User> queryUserOrder();
}

4.3、Mapper接口

这个mapper.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.mapper.VideoOrderMapper">

    <resultMap id="VideoMapper" type="com.pojo.VideoOrder">
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result property="outTradeNo" column="outTradeNo"/>
        <result property="createTime" column="createTime"/>
        <result property="state" column="state"/>
        <result property="totalFee" column="totalFee"/>
        <result property="videoId" column="video_id"/>
        <result property="videoTitle" column="video_title"/>

        <association property="user" javaType="com.pojo.User">
            <id property="id" column="user_id"/>
            <result property="name" column="name"/>
            <result property="headImg" column="head_img"/>
            <result property="createTime" column="create_time"/>
            <result property="phone" column="phone"/>
        </association>
    </resultMap>

    <select id="queryViddeoOrderList" resultMap="VideoMapper">
        select
          o.id id,
          o.user_id,
          o.out_trade_no,
          o.state,
          o.total_fee,
          o.video_id,
          o.video_id,
          o.video_title,
          u.name,
          u.head_img,
          u.create_time,
          u.phone
        from video_order o
        left join user u
        on o.user_id=u.id
    </select>
    
    <resultMap id="UserOrderResultMap" type="com.pojo.User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="headImg" column="head_img"/>
        <result property="createTime" column="create_time"/>
        <result property="phone" column="phone"/>
        <!--
            property填写pojo类中集合类属性的名称
            ofType 集合中的pojo类型
        -->
        <collection property="videoOrderList" ofType="com.pojo.VideoOrder">
            <id column="order_id" property="id"/>
            <result column="user_id" property="userId"/>
            <result property="outTradeNo" column="outTradeNo"/>
            <result property="createTime" column="createTime"/>
            <result property="state" column="state"/>
            <result property="totalFee" column="totalFee"/>
            <result property="videoId" column="video_id"/>
            <result property="videoTitle" column="video_title"/>
        </collection>
    </resultMap>
    
    <select id="queryUserOrder" resultMap="UserOrderResultMap">
        select
          u.id,u.name,u.head_img,u.create_time,u.phone,
          o.id orderid,o.out_trade_no,o.user_id,o.create_time,o.state,o.total_fee,o.video_id,o.video_title,o.video_img
        from user u left join video_order o
        on u.id=o.user_id
    </select>
</mapper>

4.4、配置文件

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!--加载properties文件-->
    <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>

    <!--配置数据源和连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--配置数据源四要素-->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--1: 配置连接池-->
        <property name="dataSource" ref="dataSource"/>
        <!--2: 管理mapper.xml文件-->
        <property name="mapperLocations" value="classpath:com.mapper/*Mapper.xml"/>
        <!--3: 配置别名-->
        <property name="typeAliasesPackage" value="com.pojo"/>
        <!--4: 配置个性化设置-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--Mapper接口扫描器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.mapper"/>
    </bean>

    <!--组件扫描器-->
    <context:component-scan base-package="com"/>

    <!--业务层的事务配置:3W-->
    <!--What: 做什么增强(事务增强)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--WHen: 在什么时候做事务增强(在发生增删改查操作的时候做增强)-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!--读数据时不开启事务-->
            <tx:method name="get*" read-only="true"/><!--只读不开启事务-->
            <tx:method name="list*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <!--其他操作都开启事务-->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--Where: 在哪里做事务增强(在service层所有的增删改查操作中做增强)-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.service.impl.*ServiceImpl.*(..))"/>
        <!--连接Where和When-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/cdclass?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.cn.wolfcode=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

mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">


    <!--引入其他配置文件-->
    <import resource="applicationContext.xml"/>
    <!--配置注解驱动-->
    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

mybatis-config.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>

    <!--MyBatis的个性化配置-->
    <settings>
        <setting name="lazyLoadingEnabled" value="false"/>
        <setting name="aggressiveLazyLoading" value="false"/>
        <setting name="lazyLoadTriggerMethods" value="clone"/>
    </settings>
    
</configuration>

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>com.thxy</groupId>
    <artifactId>ssm</artifactId>
    <version>1.0</version>

    <!--设置项目打包方式-->
    <packaging>war</packaging>

    <!--配置各个环境的属性(版本号)-->
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.0.8.RELEASE</spring.version>
        <mybatis.version>3.4.5</mybatis.version>
    </properties>

    <dependencies>
        <!-- JUnit4测试工具 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

        <!-- servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- jstl标签库 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>


        <!-- druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.26</version>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>

        <!-- mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!-- lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.6</version>
            <scope>provided</scope>
        </dependency>

        <!--分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
        </dependency>

        <!-- json转换工具 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2-beta-5</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <!--Tomcat服务器插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <uriEncoding>UTF-8</uriEncoding>
                    <path>/</path>
                    <port>8080</port>
                </configuration>
            </plugin>

            <!--Java编译器插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>



            <!--mybatis的generator插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <verbose>true</verbose>
                    <!-- 代表mybatis generator生成的内容不要覆盖已有的内容 -->
                    <overwrite>false</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.15</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。