多次缓存和懒加载你都懂了吗?
一、一级缓存
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>
- 点赞
- 收藏
- 关注作者
评论(0)