← Назад к вопросам

Попадет ли сущность в кэш первого уровня при вызове save

1.8 Middle🔥 201 комментариев
#ORM и Hibernate#Кэширование и NoSQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Попадёт ли сущность в кэш первого уровня при вызове save?

Да, сущность попадёт в кэш первого уровня (First-Level Cache, Session Cache) при вызове метода save() в Hibernate/JPA.

Что такое кэш первого уровня

Кэш первого уровня — это механизм кэширования, встроенный в persistence context (Session в Hibernate или EntityManager в JPA). Он существует на уровне сессии/транзакции и автоматически управляется Hibernate.

Как это работает

@Service
@Transactional
public class UserService {
    @Autowired
    private EntityManager em;
    
    public void saveAndRetrieveUser() {
        User user = new User();
        user.setName("John");
        
        // 1. Вызываем save() - сущность попадает в кэш первого уровня
        em.persist(user);  // или repository.save(user)
        
        // 2. На этом этапе сущность уже в кэше первого уровня
        // Её id может быть ещё null (зависит от стратегии генерации)
        
        // 3. Вторичный поиск из кэша, БД не затронута
        User retrieved = em.find(User.class, user.getId());
        // Или через JPQL
        Query query = em.createQuery("SELECT u FROM User u WHERE u.id = :id");
        query.setParameter("id", user.getId());
        User found = (User) query.getSingleResult();
        // retrieved == found == user (один объект!)
    }
}

Жизненный цикл при save

Этап 1: Transient (новая сущность)

User user = new User();
user.setName("John");
// Объект существует только в памяти, в кэше нет

Этап 2: Persist/Save (добавление в кэш)

em.persist(user);  // или userRepository.save(user)
// Объект переходит в состояние Managed
// Попадает в кэш первого уровня (persistence context)
// SQL INSERT ещё не выполнен

Этап 3: В кэше, ждёт flush

// Объект в памяти и в кэше, но SQL ещё не отправлен в БД
User same = em.find(User.class, user.getId()); // Из кэша

Этап 4: Flush и commit

// При завершении транзакции происходит flush
// SQL INSERT выполняется в этот момент
// После commit объект остаётся Managed до закрытия сессии

Доказательство через пример

@Service
public class CacheTest {
    @Autowired
    private EntityManager em;
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void demonstrateFirstLevelCache() {
        User user = new User("Alice");
        
        // 1. Сохраняем
        User saved = userRepository.save(user);
        System.out.println("User ID after save: " + saved.getId());
        // ID может быть null в зависимости от GenerationType
        
        // 2. Получаем из БД - на самом деле из кэша
        User found = userRepository.findById(saved.getId()).orElse(null);
        
        // 3. Это один и тот же объект
        System.out.println(saved == found);  // true - один объект из кэша!
        
        // 4. Меняем имя
        saved.setName("Bob");
        
        // 5. Выполняем UPDATE БД - нужно вызвать flush или saveAndFlush
        userRepository.saveAndFlush(saved);
        
        // Теперь в БД имя Bob, и объект всё ещё в кэше первого уровня
    }
}

Важные моменты

Identity vs Equality Oбъекты, вернутые из кэша первого уровня, — один и тот же объект (identity), не копия.

User u1 = repository.save(user);
User u2 = repository.findById(user.getId()).orElse(null);
u1 == u2  // true, один объект
u1.equals(u2)  // true

GenerationType влияет на ID

@GeneratedValue(strategy = GenerationType.IDENTITY)
// ID генерируется БД, поэтому fill происходит при flush

@GeneratedValue(strategy = GenerationType.SEQUENCE)
// ID генерируется до сохранения, доступен сразу

Scope кэша первого уровня Кэш существует в рамках persistence context (обычно = одна транзакция).

@Transactional
public void method1() {
    User u = repository.save(user);
    // В кэше первого уровня
}
// После завершения транзакции persistence context закрывается
// Кэш первого уровня очищается

public void method2() {
    // Новая транзакция = новый persistence context = новый кэш
    User u = repository.findById(id);
    // Будет запрос к БД, кэша нет
}

Отличие от кэша второго уровня (L2 Cache)

Кэш первого уровня:

  • Встроенный, всегда работает
  • Уровень сессии/транзакции
  • Управляется автоматически
  • Scope: EntityManager/Session

Кэш второго уровня (L2 Cache):

  • Опциональный (нужно подключать)
  • Уровень SessionFactory (приложения)
  • Требует конфигурации (Ehcache, Redis)
  • Scope: всё приложение
// L2 Cache нужно явно включить
@Cacheable
@Entity
public class User { ... }

Вывод

Да, сущность обязательно попадёт в кэш первого уровня при вызове save()/persist(). Это автоматический механизм Hibernate/JPA, который повышает производительность, избегая повторных запросов к БД в рамках одной транзакции.