本文目录导读:

- 项目基础结构
- 实体类
- Mapper接口和XML
- Mapper XML文件
- 业务层演示缓存
- MyBatis配置文件
- Redis二级缓存实现(生产环境)
- 使用Redis缓存的Mapper
- 缓存测试类
- 核心配置文件
- 关键要点说明
我将为您提供一个完整的MyBatis缓存案例,涵盖一级缓存、二级缓存的基础用法,以及更实用的生产环境缓存方案(使用Redis)。
项目基础结构
src/main/java/com/example/cache/
├── entity/
│ ├── User.java
│ └── Order.java
├── mapper/
│ ├── UserMapper.java
│ ├── UserMapper.xml
│ ├── OrderMapper.java
│ └── OrderMapper.xml
├── service/
│ ├── UserService.java
│ └── UserServiceImpl.java
└── config/
├── MyBatisConfig.java
└── RedisConfig.java
实体类
// User.java
package com.example.cache.entity;
import java.io.Serializable;
import java.time.LocalDateTime;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createTime;
// getter/setter 省略
}
// Order.java
package com.example.cache.entity;
import java.io.Serializable;
import java.math.BigDecimal;
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private Long userId;
private String orderNo;
private BigDecimal amount;
// getter/setter 省略
}
Mapper接口和XML
// UserMapper.java
package com.example.cache.mapper;
import com.example.cache.entity.User;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
@CacheNamespace(eviction = org.apache.ibatis.cache.decorators.LruCache.class,
flushInterval = 60000,
size = 1024,
readWrite = true)
public interface UserMapper {
User selectUserById(@Param("id") Long id);
int insertUser(User user);
int updateUser(User user);
int deleteUser(@Param("id") Long id);
}
// OrderMapper.java
package com.example.cache.mapper;
import com.example.cache.entity.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface OrderMapper {
Order selectOrderById(@Param("id") Long id);
int insertOrder(Order order);
}
Mapper XML文件
<!-- 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.example.cache.mapper.UserMapper">
<!-- 查询用户,使用二级缓存 -->
<select id="selectUserById" resultType="User" useCache="true">
SELECT * FROM t_user WHERE id = #{id}
</select>
<!-- 插入用户,刷新缓存 -->
<insert id="insertUser" parameterType="User" flushCache="true">
INSERT INTO t_user (username, email, age, create_time)
VALUES (#{username}, #{email}, #{age}, #{createTime})
</insert>
<!-- 更新用户,刷新缓存 -->
<update id="updateUser" parameterType="User" flushCache="true">
UPDATE t_user
SET username = #{username},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
<!-- 删除用户,刷新缓存 -->
<delete id="deleteUser" parameterType="Long" flushCache="true">
DELETE FROM t_user WHERE id = #{id}
</delete>
</mapper>
<!-- OrderMapper.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.example.cache.mapper.OrderMapper">
<!-- 不使用缓存 -->
<select id="selectOrderById" resultType="Order" useCache="false">
SELECT * FROM t_order WHERE id = #{id}
</select>
<insert id="insertOrder" parameterType="Order">
INSERT INTO t_order (user_id, order_no, amount)
VALUES (#{userId}, #{orderNo}, #{amount})
</insert>
</mapper>
业务层演示缓存
// UserService.java
package com.example.cache.service;
import com.example.cache.entity.User;
public interface UserService {
User getUserById(Long id);
User getUserWithCache(Long id);
void updateUser(User user);
void deleteUser(Long id);
}
// UserServiceImpl.java
package com.example.cache.service;
import com.example.cache.entity.User;
import com.example.cache.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(readOnly = true)
public User getUserById(Long id) {
// 一级缓存:同一个SqlSession内缓存
log.info("第一次查询,写入一级缓存");
User user1 = userMapper.selectUserById(id);
log.info("第二次查询,命中一级缓存");
User user2 = userMapper.selectUserById(id);
// 在同一个事务中,user1 和 user2 应该是同一个实例(一级缓存)
log.info("user1 == user2 : {}", user1 == user2);
return user2;
}
@Override
@Transactional(readOnly = true)
public User getUserWithCache(Long id) {
// 演示二级缓存:需要每次调用新的SqlSession
log.info("Service层第一次调用(可能触发二级缓存)");
User user1 = userMapper.selectUserById(id);
// 模拟业务逻辑
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Service层第二次调用(验证二级缓存)");
User user2 = userMapper.selectUserById(id);
return user2;
}
@Override
@Transactional
public void updateUser(User user) {
// 更新操作会清除对应Mapper的二级缓存
log.info("更新用户信息,同时清除二级缓存");
userMapper.updateUser(user);
}
@Override
@Transactional
public void deleteUser(Long id) {
userMapper.deleteUser(id);
}
}
MyBatis配置文件
<!-- 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>
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 配置日志 -->
<setting name="logImpl" value="SLF4J"/>
<!-- 懒加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
</configuration>
Redis二级缓存实现(生产环境)
// RedisCache.java
package com.example.cache.config;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.DigestUtils;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RedisCache implements Cache {
private final String id;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public RedisCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = SpringContextUtils.getBean("redisTemplate");
if (redisTemplate != null) {
// 添加前缀,防止key冲突
String redisKey = getRedisKey(key);
redisTemplate.opsForValue().set(redisKey, value, 30, TimeUnit.MINUTES);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = SpringContextUtils.getBean("redisTemplate");
if (redisTemplate != null) {
String redisKey = getRedisKey(key);
return redisTemplate.opsForValue().get(redisKey);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Object removeObject(Object key) {
try {
RedisTemplate redisTemplate = SpringContextUtils.getBean("redisTemplate");
if (redisTemplate != null) {
String redisKey = getRedisKey(key);
return redisTemplate.delete(redisKey);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public void clear() {
try {
RedisTemplate redisTemplate = SpringContextUtils.getBean("redisTemplate");
if (redisTemplate != null) {
// 清除该namespace下的所有缓存
Set<String> keys = redisTemplate.keys(id + "*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getSize() {
try {
RedisTemplate redisTemplate = SpringContextUtils.getBean("redisTemplate");
if (redisTemplate != null) {
Long size = redisTemplate.opsForList().size(id + ":size");
return size == null ? 0 : size.intValue();
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
private String getRedisKey(Object key) {
String keyString = String.valueOf(key);
// 使用MD5生成key,避免key过长
String md5Key = DigestUtils.md5DigestAsHex(keyString.getBytes());
return id + ":" + md5Key;
}
}
// SpringContextUtils.java
package com.example.cache.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
使用Redis缓存的Mapper
// UserMapperWithRedis.java
package com.example.cache.mapper;
import com.example.cache.config.RedisCache;
import com.example.cache.entity.User;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
@CacheNamespace(implementation = RedisCache.class,
eviction = org.apache.ibatis.cache.decorators.LruCache.class,
flushInterval = 60000,
size = 1024,
readWrite = true)
public interface UserMapperWithRedis {
User selectUserById(@Param("id") Long id);
int insertUser(User user);
}
缓存测试类
// CacheTest.java
package com.example.cache;
import com.example.cache.entity.User;
import com.example.cache.mapper.UserMapper;
import com.example.cache.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SpringBootTest
public class CacheTest {
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@Test
public void testFirstLevelCache() {
// 测试一级缓存(SqlSession级别)
log.info("=== 测试一级缓存 ===");
User user = userService.getUserById(1L);
log.info("查询结果: {}", user);
}
@Test
public void testSecondLevelCache() {
// 测试二级缓存(Mapper级别)
log.info("=== 测试二级缓存 ===");
// 第一次查询,会查询数据库并写入二级缓存
User user1 = userMapper.selectUserById(1L);
log.info("第一次查询: {}", user1);
// 模拟不同SqlSession查询
User user2 = userMapper.selectUserById(1L);
log.info("第二次查询: {}", user2);
// 验证是否为同一个实例
log.info("user1 == user2 : {}", user1 == user2);
}
@Test
public void testCacheUpdate() {
// 测试更新后缓存失效
log.info("=== 测试缓存更新 ===");
// 查询缓存
User user = userMapper.selectUserById(1L);
log.info("查询缓存: {}", user);
// 更新
user.setUsername("updated_user");
userMapper.updateUser(user);
// 再次查询,应该触发缓存刷新
User userAfterUpdate = userMapper.selectUserById(1L);
log.info("更新后查询: {}", userAfterUpdate);
}
@Test
public void testPerformanceComparison() {
// 测试缓存性能提升
log.info("=== 测试缓存性能 ===");
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
userMapper.selectUserById(1L);
}
long end = System.currentTimeMillis();
log.info("100次查询耗时: {} ms", (end - start));
}
}
核心配置文件
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
database: 0
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.cache.entity
configuration:
cache-enabled: true
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
关键要点说明
一级缓存特性
- 作用范围:SqlSession级别
- 默认开启:无法关闭
- 失效条件:执行增删改操作后自动清除
二级缓存特性
- 作用范围:Mapper级别(namespace级别)
- 需要开启:在配置文件或注解中开启
- 跨SqlSession:多个SqlSession可以共享
- 序列化要求:实体类需要实现Serializable
缓存策略
- LRU(Least Recently Used):最近最少使用
- FIFO:先进先出
- SOFT:软引用
- WEAK:弱引用
生产环境建议
- 数据量小的基础数据表使用二级缓存
- 频繁更新的数据不使用缓存
- 敏感数据(如金融数据)避免缓存
- 使用Redis等分布式缓存替代本地缓存
这个案例涵盖了MyBatis缓存的所有关键内容,从基础用法到生产环境实践,可以直接集成到Spring Boot项目中。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播