Skip to content

缓存机制详解:JPA、Hibernate、Redis、JCache 的关系

本文档详细解释 Spring Data JPA、Hibernate、Redis、JCache 之间的关系,以及 MyBatis 的缓存机制。

1. Spring Data JPA 和 Hibernate JCache 的关系

Spring Data JPA 的本质

你的理解:Spring Data JPA 是简单处理数据库数据写 SQL 语句执行的

实际情况:Spring Data JPA 是一个抽象层,它本身不直接执行 SQL,而是:

  1. 提供统一的 Repository 接口

    java
    public interface UserRepository extends JpaRepository<User, Long> {
        // Spring Data JPA 提供的方法
        User findByUsername(String username);
    }
  2. 底层使用 ORM 框架(默认是 Hibernate)

    • Spring Data JPA 是规范(JPA 规范)
    • Hibernate 是实现(JPA 规范的实现之一)
    • 关系:Spring Data JPA → JPA 规范 → Hibernate 实现
  3. 执行流程

    你的代码:userRepository.findByUsername("admin")
    
    Spring Data JPA:解析方法名,生成查询
    
    Hibernate:将查询转换为 SQL
    
    数据库:执行 SQL
    
    Hibernate:将结果映射为 Java 对象
    
    返回给你:User 对象

Hibernate 的二级缓存(JCache)

Hibernate 有两级缓存

一级缓存(Session 缓存)

  • 作用域:单个 Session(事务)
  • 自动管理:Hibernate 自动管理,无需配置
  • 生命周期:事务结束即清除
  • 示例
    java
    // 第一次查询,访问数据库
    User user1 = session.get(User.class, 1L);
    
    // 第二次查询,从一级缓存获取,不访问数据库
    User user2 = session.get(User.class, 1L);

二级缓存(JCache)

  • 作用域:跨 Session,应用级别
  • 需要配置:需要显式配置(如 EhCache、Redis 等)
  • 生命周期:应用运行期间
  • 示例
    java
    // Session 1:第一次查询,访问数据库,存入二级缓存
    User user1 = session1.get(User.class, 1L);
    
    // Session 2:从二级缓存获取,不访问数据库
    User user2 = session2.get(User.class, 1L);

为什么需要 Hibernate JCache?

JCache (JSR-107) 是 Java 缓存规范:

  • 定义了统一的缓存 API
  • 支持多种缓存实现(EhCache、Redis、Caffeine 等)
  • Hibernate 通过 JCache 接口使用缓存

关系链

Spring Data JPA

Hibernate (ORM)

JCache (缓存规范)

EhCache/Redis (缓存实现)

为什么配置了 EhCache 还需要 hibernate-jcache?

  • ehcache 依赖:提供 EhCache 缓存实现
  • hibernate-jcache 依赖:提供 Hibernate 和 JCache 之间的桥接
  • 没有 hibernate-jcache,Hibernate 无法使用 JCache 规范的缓存

2. Redis Repository 是什么?

Redis Repository 的概念

Redis Repository 是 Spring Data Redis 提供的功能,类似于 JPA Repository:

java
// JPA Repository(你当前使用的)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

// Redis Repository(如果使用)
@Repository
public interface UserRedisRepository extends CrudRepository<User, String> {
    User findByUsername(String username);
}

Redis Repository 的特点

  1. 数据存储:数据存储在 Redis 中,不是数据库
  2. 使用场景
    • 临时数据(如会话、购物车)
    • 缓存数据(如热点数据)
    • 不需要持久化的数据
  3. 序列化:需要将对象序列化为 Redis 可存储的格式

为什么会冲突?

问题场景

你的项目结构:
com.alisunxin.api.infrastructure.dao.repository/
    ├── ActivityRepository.java (JPA Repository)
    ├── UserRepository.java (JPA Repository)
    └── ...

Spring Boot 启动时:
1. Spring Data JPA 扫描:找到 ActivityRepository,识别为 JPA Repository ✅
2. Spring Data Redis 扫描:也扫描到 ActivityRepository,尝试识别为 Redis Repository ❌
3. 发现 ActivityRepository 继承的是 JpaRepository,不是 Redis Repository
4. 发出警告:无法安全识别为 Redis Repository

为什么会扫描?

  • Spring Boot 的自动配置会扫描所有 Repository 接口
  • 如果同时引入了 spring-boot-starter-data-jpaspring-boot-starter-data-redis
  • 两者都会尝试识别 Repository

解决方案

yaml
spring:
  data:
    redis:
      repositories:
        enabled: false  # 禁用 Redis Repository 自动配置

Redis 的两种使用方式

方式 1:Redis Repository(你不需要)

java
@Repository
public interface UserRedisRepository extends CrudRepository<User, String> {
    // 数据存储在 Redis,不是数据库
}

方式 2:RedisTemplate(你当前使用的)

java
@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void cacheUser(User user) {
    redisTemplate.opsForValue().set("user:" + user.getId(), user);
}

你的项目使用的是方式 2,所以不需要 Redis Repository。

3. MyBatis 的缓存机制

MyBatis 的缓存

MyBatis 也有两级缓存

一级缓存(SqlSession 缓存)

  • 作用域:单个 SqlSession
  • 自动管理:MyBatis 自动管理
  • 生命周期:SqlSession 关闭即清除
  • 示例
    java
    // 第一次查询
    User user1 = sqlSession.selectOne("getUserById", 1L);
    
    // 第二次查询,从一级缓存获取
    User user2 = sqlSession.selectOne("getUserById", 1L);

二级缓存(Mapper 级别缓存)

  • 作用域:Mapper 级别,跨 SqlSession
  • 需要配置:需要在 Mapper.xml 中启用
  • 默认实现:内存缓存(HashMap)
  • 配置示例
    xml
    <!-- MyBatis 配置 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
    <!-- Mapper.xml -->
    <mapper namespace="com.example.UserMapper">
        <cache/>  <!-- 启用二级缓存 -->
        
        <select id="getUserById" resultType="User">
            SELECT * FROM user WHERE id = #{id}
        </select>
    </mapper>

MyBatis vs Hibernate 缓存对比

特性MyBatisHibernate
一级缓存SqlSession 级别Session 级别
二级缓存Mapper 级别,默认内存应用级别,支持多种实现
配置方式Mapper.xml 中 <cache/>Hibernate 配置 + JCache
缓存实现默认 HashMap,可扩展支持 EhCache、Redis 等
缓存粒度SQL 级别Entity 级别
自动管理需要手动配置自动管理 Entity 缓存

为什么 MyBatis 不需要 JCache?

MyBatis 的缓存机制

  1. 默认实现:使用内存(HashMap)作为二级缓存
  2. 扩展性:可以实现 Cache 接口自定义缓存
  3. 不需要 JCache:MyBatis 有自己的缓存接口,不依赖 JCache 规范

MyBatis 自定义缓存示例

java
public class RedisCache implements Cache {
    // 实现 Cache 接口,使用 Redis 作为缓存
}
xml
<!-- 使用自定义缓存 -->
<cache type="com.example.RedisCache"/>

为什么 Hibernate 需要 JCache?

Hibernate 的设计理念

  1. 标准化:遵循 JCache (JSR-107) 标准
  2. 灵活性:可以切换不同的缓存实现(EhCache、Redis、Caffeine)
  3. 统一接口:通过 JCache 接口,无需修改代码即可切换缓存实现

对比

MyBatis:
  自定义 Cache 接口 → 需要为每个缓存实现编写适配器

Hibernate:
  JCache 标准接口 → 所有 JCache 实现都可以直接使用

总结

1. Spring Data JPA 和 Hibernate JCache

Spring Data JPA(抽象层)

Hibernate(ORM 实现)

JCache(缓存规范)

EhCache(缓存实现)
  • Spring Data JPA 不直接执行 SQL,而是通过 Hibernate
  • Hibernate 使用 JCache 规范来支持二级缓存
  • 需要 hibernate-jcache 作为桥接

2. Redis Repository

  • Redis Repository 是 Spring Data Redis 提供的功能
  • 类似于 JPA Repository,但数据存储在 Redis
  • 你的项目不需要,因为使用的是 RedisTemplate
  • 可以通过配置禁用,避免误识别 JPA Repository

3. MyBatis 缓存

  • MyBatis 有自己的缓存机制
  • 一级缓存:SqlSession 级别(自动)
  • 二级缓存:Mapper 级别(需要配置)
  • 默认使用内存缓存,可以自定义实现
  • 不需要 JCache,因为有自己的缓存接口

你的项目架构

应用层缓存(Redis)

    RedisTemplate → 缓存用户信息、会话等

数据库查询缓存(EhCache)

    Hibernate JCache → 缓存数据库查询结果

数据库

    MySQL → 持久化存储

三者各司其职

  • Redis:应用层缓存(快速访问热点数据)
  • EhCache:数据库查询缓存(减少数据库访问)
  • MySQL:持久化存储(数据最终存储)

常见问题

Q1: 为什么 Hibernate 需要 JCache,而 MyBatis 不需要?

A:

  • Hibernate 遵循 JCache 标准,可以灵活切换缓存实现
  • MyBatis 有自己的缓存接口,不依赖标准

Q2: Redis 和 EhCache 可以同时使用吗?

A: 可以!它们用途不同:

  • Redis:应用层缓存(分布式缓存)
  • EhCache:Hibernate 二级缓存(本地缓存)

Q3: 为什么配置了 EhCache 还需要 hibernate-jcache?

A:

  • ehcache:提供缓存实现
  • hibernate-jcache:提供 Hibernate 和 JCache 之间的桥接
  • 两者缺一不可

Q4: Redis Repository 和 RedisTemplate 有什么区别?

A:

  • Redis Repository:类似 JPA Repository,自动 CRUD,数据存储在 Redis
  • RedisTemplate:手动操作 Redis,更灵活,你当前使用的

Q5: MyBatis 的缓存和 Hibernate 的缓存哪个更好?

A: 各有优势:

  • MyBatis:缓存粒度更细(SQL 级别),更可控
  • Hibernate:缓存更智能(Entity 级别),自动管理关联关系

参考资料

Powered by VitePress