Почему @Cacheable с ключом по toString() ломается на прокси-объектах

Spring Cache по умолчанию строит ключ из аргументов метода через SimpleKeyGenerator. Если аргумент — доменный объект без переопределённого equals()/hashCode(), а команда решила упростить и вручную задать ключ через toString() в SpEL, начинаются проблемы там, где их не ждали.

Проблема всплывает, когда аргумент — не обычный объект, а lazy-proxy Hibernate. toString() у прокси до инициализации возвращает что-то вроде идентификатора класса и хеша, а после инициализации — уже другую строку с реальными полями. Один и тот же логический объект даёт два разных ключа кэша в зависимости от того, был ли он уже подгружен из БД.

@Cacheable( value = "orders", key = "#customer.toString()" ) public Order findOrder(Customer customer) { return repository.find(customer); }

На собесе спросят: как правильно задать ключ вместо toString()? Явно указать конкретные поля через SpEL — key = "#customer.id" — или написать свой KeyGenerator, который работает с бизнес-идентификатором, а не с представлением объекта в памяти. Прокси тут вообще не должен участвовать в вычислении ключа.

Кэш с нестабильным ключом хуже отсутствия кэша: он молча создаёт дубликаты записей и никогда не отдаёт cache hit там, где должен.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки