Hibernate 缓存机制是为了提高数据库访问性能,通过减少对数据库的直接访问次数来实现的。Hibernate 提供了两级缓存机制:一级缓存(Session 级别)和二级缓存(SessionFactory 级别)。
一级缓存
- 范围:一级缓存是 Hibernate 默认启用的,会话级别(Session)。每个 Session 都有一个独立的一级缓存。
- 特点:在同一个 Session 中,重复读取同一条数据时,Hibernate 会直接从缓存中获取,而不是再次查询数据库。
- 实现:通过一个 Map 结构来保存实体对象,使用实体的主键作为键。
二级缓存
- 范围:二级缓存是跨越会话的缓存,需要显式配置,并且在 SessionFactory 级别共享。
- 特点:适用于需要在多个 Session 之间共享的持久化对象,通过某些第三方缓存提供者(如 EhCache、OSCache、Infinispan 等)实现。
- 实现:通过缓存提供者的具体实现来管理和维护缓存内容,包括对象生命周期、缓存策略等。
底层原理
-
缓存存储:
- 一级缓存存储在内存中的一个 HashMap 中,这个 HashMap 是 Session 对象的一部分。
- 二级缓存的存储依赖于具体的缓存提供者,可以存储在内存中,也可以持久化到磁盘。
-
缓存操作:
- 当执行查询操作时,首先检查一级缓存,如果命中则直接返回结果;否则,再检查二级缓存。
- 如果二级缓存也未命中,则执行实际的数据库查询,并将结果分别放入一级缓存和二级缓存。
-
缓存同步:
- 对于一级缓存来说,同步是自动的,因为它只存在于当前 Session 中。
- 对于二级缓存,Hibernate 提供了多种缓存策略(如读写、非严格读写、只读、事务性),不同策略有不同的同步机制。
-
缓存失效:
- 一级缓存随着 Session 的关闭而失效。
- 二级缓存具有可配置的失效策略,如基于时间的失效、LRU(最近最少使用)算法等。
示例流程
-
读取数据:
- 首先尝试从一级缓存读取,如果找到则返回。
- 否则,尝试从二级缓存读取,如果找到则返回,并放到一级缓存中。
- 如果都找不到,则执行数据库查询,将结果放到一级缓存和二级缓存中。
-
更新数据:
- 更新操作会使一级缓存中的相应实体失效(因为数据已修改,需要重新加载)。
- 同时通知二级缓存,更新或清除相应的缓存条目。
总的来说,Hibernate 的缓存机制通过减少数据库访问次数,提高了应用程序的性能,但同时需要注意缓存一致性、失效策略等问题以确保数据的正确性和实时性。
Hibernate 缓存机制的源码实现涉及到多个模块和类。以下是对其核心部分的简要介绍,以帮助理解其工作原理。
一级缓存(Session Cache)
一级缓存是在 Session 级别实现的,默认情况下是开启的,且每个 Session 都有独立的缓存。
关键类
org.hibernate.engine.spi.SessionImplementororg.hibernate.internal.SessionImpl
示例代码
public class SessionImpl implements Session {
private EntityEntryContext entityEntryContext = new StatefulPersistenceContext(this);
@Override
public Object get(Class<?> clazz, Serializable id) {
// 从一级缓存中读取对象
Object cachedEntity = entityEntryContext.getEntry(id);
if (cachedEntity != null) {
return cachedEntity;
}
// 如果一级缓存中没有,则查询数据库
Object entity = databaseQuery(clazz, id);
entityEntryContext.addEntry(id, entity);
return entity;
}
}
二级缓存(SessionFactory Cache)
二级缓存是可选的,需要在配置文件中显式启用,并可以选择不同的缓存提供者,如 EHCache、Infinispan 等。
关键类
org.hibernate.cache.spi.CacheImplementororg.hibernate.cache.internal.StandardCacheImplementororg.hibernate.cfg.Settingsorg.hibernate.cache.spi.RegionFactory
配置示例
在 hibernate.cfg.xml 中配置二级缓存:
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
<property name="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>
实现逻辑
- 缓存实体:使用
CacheConcurrencyStrategy来管理并发访问。 - 缓存区域:通过
Region接口定义缓存区域。
示例代码
以下是一个简单的示例,展示如何使用 EHCache 实现二级缓存:
public class EhCacheRegionFactory implements RegionFactory {
private CacheManager cacheManager;
@Override
public void start(Settings settings, Properties properties) throws CacheException {
this.cacheManager = CacheManager.create(properties.getProperty("hibernate.cache.provider_configuration_file_resource_path"));
}
@Override
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
net.sf.ehcache.Cache cache = cacheManager.getCache(regionName);
if (cache == null) {
cacheManager.addCache(regionName);
cache = cacheManager.getCache(regionName);
}
return new EhCacheEntityRegion(cache, regionName, metadata);
}
}
缓存操作流程
-
查询数据:
- Hibernate 首先会检查一级缓存,如果命中则直接返回。
- 如果一级缓存未命中,再检查二级缓存。
- 如果二级缓存也未命中,则查询数据库,并将结果分别放入一级缓存和二级缓存。
-
更新数据:
- 更新操作会使一级缓存中的相应实体无效。
- 通知二级缓存更新或清除对应的缓存条目。
示例代码片段
以下是一个完整生命周期中对缓存的处理:
// 获取SessionFactory
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
// 开始一个session
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// 查询操作,首先检查一级缓存
MyEntity entity = session.get(MyEntity.class, 1);
// 保存操作,更新一级缓存
entity.setSomeField("newValue");
session.saveOrUpdate(entity);
// 提交事务,通知二级缓存同步
transaction.commit();
session.close();
// 在新的session中再次查询,检查二级缓存
Session newSession = sessionFactory.openSession();
MyEntity newEntity = newSession.get(MyEntity.class, 1);
newSession.close();
总结
Hibernate 的缓存机制通过一级缓存和二级缓存减少了直接访问数据库的次数,从而提高了应用程序的性能。一级缓存是会话级别的,默认启用;二级缓存需要显式配置,并允许跨会话共享缓存数据。理解这些缓存机制及其源码实现,有助于更好地优化和调试 Hibernate 应用程序。