← Назад к вопросам
Как работает кэширование в Hibernate?
2.3 Middle🔥 201 комментариев
#ORM и Hibernate#Кэширование и NoSQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает кэширование в Hibernate
Кэширование в Hibernate - это мощный механизм оптимизации производительности, который снижает количество запросов к базе данных. Существует несколько уровней кэширования.
1. First-Level Cache (L1 Cache) - уровень Session
Это встроенный кэш Hibernate, работает на уровне Session:
// Сессия - это транзакция с БД
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
// Первый запрос к БД
User user1 = session.get(User.class, 1L);
System.out.println(user1.getName()); // SQL запрос выполнен
// Второй запрос - берет из кэша Session (L1)
User user2 = session.get(User.class, 1L);
System.out.println(user2.getName()); // SQL НЕ выполнен! Из кэша
// Это один и тот же объект в памяти
System.out.println(user1 == user2); // true
tx.commit();
session.close(); // L1 кэш очищается
2. Second-Level Cache (L2 Cache)
Глобальный кэш для всей SessionFactory, может быть общим между сессиями:
// Конфигурация в persistence.xml
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.cache.jcache.JCacheRegionFactory"/>
// Или в Java конфигурации
@Configuration
public class HibernateConfig {
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
Properties properties = new Properties();
properties.setProperty(
"hibernate.cache.use_second_level_cache", "true"
);
properties.setProperty(
"hibernate.cache.region.factory_class",
"org.hibernate.cache.jcache.JCacheRegionFactory"
);
factory.setHibernateProperties(properties);
return factory;
}
}
Маркировка сущности для кэширования:
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "users")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
private Long id;
private String name;
private String email;
// getters, setters...
}
3. Использование L2 Cache
SessionFactory sessionFactory = // инициализация
// Первая сессия
Session session1 = sessionFactory.openSession();
User user1 = session1.get(User.class, 1L);
System.out.println(user1.getName()); // SQL запрос
session1.close(); // L1 кэш очищается, но L2 остается
// Вторая сессия
Session session2 = sessionFactory.openSession();
User user2 = session2.get(User.class, 1L);
System.out.println(user2.getName()); // SQL НЕ выполнен! Из L2 кэша
session2.close();
// Это разные объекты, но содержат одинаковые данные
System.out.println(user1 == user2); // false
System.out.println(user1.getId().equals(user2.getId())); // true
4. Стратегии кэширования (CacheConcurrencyStrategy)
// READ_ONLY - только для чтения, не обновляется
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Role {
// Никогда не меняется
}
// READ_WRITE - читаемое и записываемое кэширование
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
// Может обновляться, Hibernate управляет инвалидацией
}
// NONSTRICT_READ_WRITE - менее строгое, лучшая производительность
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Product {
// Может быть временная несогласованность, но быстро
}
// TRANSACTIONAL - для JTA транзакций
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class Order {
// Требует JTA
}
5. Кэширование коллекций и связей
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Order> orders = new HashSet<>();
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Order {
@Id
private Long id;
@ManyToOne
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private User user;
}
6. Query Cache - кэширование результатов запросов
// Конфигурация
<property name="hibernate.cache.use_query_cache" value="true"/>
// Использование
Session session = sessionFactory.openSession();
Query query = session.createQuery("FROM User WHERE status = :status");
query.setParameter("status", "active");
query.setCacheable(true); // Кэшируем результаты
query.setCacheRegion("activeUsers"); // Опционально указываем регион
List<User> users = query.list(); // Первый запрос к БД
List<User> users2 = query.list(); // Из кэша! Без SQL
7. Инвалидация кэша
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
User user = session.get(User.class, 1L); // Загружается в кэш
user.setName("New Name");
session.update(user); // Hibernate автоматически инвалидирует кэш
tx.commit();
session.close();
// Вторая сессия получит обновленные данные
Session session2 = sessionFactory.openSession();
User updatedUser = session2.get(User.class, 1L); // Новый SQL запрос
session2.close();
8. Ручная управление кэшем
SessionFactory sessionFactory = // инициализация
// Очистить L2 кэш для конкретной сущности
sessionFactory.getCache().evictEntity(User.class, 1L);
// Очистить весь L2 кэш для сущности
sessionFactory.getCache().evictEntityRegion(User.class);
// Очистить все коллекции
sessionFactory.getCache().evictCollectionRegion(User.class, "orders");
// Очистить весь L2 кэш
sessionFactory.getCache().evictAllRegions();
9. Пример с Spring Data JPA
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
@Id
@GeneratedValue
private Long id;
private String email;
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Cacheable("users")
Optional<User> findByEmail(String email);
}
@Service
public class UserService {
@Autowired
private UserRepository repository;
@Cacheable("user-cache")
public User getUserById(Long id) {
return repository.findById(id).orElse(null);
}
@CacheEvict("user-cache")
public void updateUser(User user) {
repository.save(user);
}
}
10. Производительность и проблемы
// ❌ Проблема: много объектов в памяти
for (int i = 0; i < 1000000; i++) {
User user = session.get(User.class, i);
// Все объекты в L1 кэше!
// Утечка памяти
}
// ✅ Решение: периодически очищайте L1 кэш
for (int i = 0; i < 1000000; i++) {
User user = session.get(User.class, i);
if (i % 100 == 0) {
session.clear(); // Очистить L1 кэш
}
}
// ❌ Проблема: устаревший кэш при изменениях вне Hibernate
update users set name = 'Updated' where id = 1; // Прямо в БД
User user = session.get(User.class, 1L); // Вернет старые данные из кэша!
// ✅ Решение: инвалидировать кэш
sessionFactory.getCache().evictEntity(User.class, 1L);
User user = session.get(User.class, 1L); // Новый запрос к БД
Иерархия кэширования
Session.get() → L1 Cache (Session) → L2 Cache → База данных
Вывод: кэширование в Hibernate работает на двух уровнях - Session (L1, автоматический) и SessionFactory (L2, требует конфигурации). Правильное использование кэширования значительно улучшает производительность, но требует понимания особенностей каждого уровня.