Есть в коде места, заросшие паутиной, куда разработчики редко заглядывают. При этом, такие места иногда напоминают о себе в самых неожиданных местах.
Когда мы активно готовились к выставке и тестировали систему, Hibernate начал бросать странное исключение collection [] was not processed by flush() triggering for unknown reasons с пометкой this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session. Гуглинг сразу вывел на закрытые тикет HHH-3876, который тоже не сильно помог. В данном сущности была парочка мутабельных коллекций, которые лениво подгружались. Стоило поставить жадную подгрузку, как исключение пропадало. Тут взгляд упал на строку:
@EqualsAndHashCode(exclude = {"tags"})
которая подменяла стандартные методы equals и hashCode. Эта аннотация принадлежит великолепной библиотеке lombok, позволяющей при компиляции генерировать boilerplate код: toString, getter, setter, equalsAndHashCode.
Тем не менее, подменять хэшкод в мутабельных классах - очень плохая идея, ведущая к крайне веселым последствиям. Как оказалось, при персисте, гибернейт дергал метод equals, тот в свою очередь пробегался по полям и задевал ленивый прокси, прокси инициализировался и изменял хэш объекта, гибернейт видел это и сходил с ума от внезапного изменения уже обработанной коллекции.
На фикс бага ушло несколько часов. Самое веселое началось, когда мы открыли историю:
А теперь, самое интересное, результаты раскопок: добавлен этот метод был для удобства тестирования. На тот момент, сущность не содержала коллекций и результаты тестов было очень удобно сравнивать с ожидаемыми объектами. Но спустя некоторое время, объект стал сложным и сравнение было переписано на toString, необходимость в подмене equals отпала. Тем не менее equals жил еще несколько месяцев, порождая трудноуловимые баги. А сколько еще таких привидений живет в коде?
Когда мы активно готовились к выставке и тестировали систему, Hibernate начал бросать странное исключение collection [] was not processed by flush() triggering for unknown reasons с пометкой this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session. Гуглинг сразу вывел на закрытые тикет HHH-3876, который тоже не сильно помог. В данном сущности была парочка мутабельных коллекций, которые лениво подгружались. Стоило поставить жадную подгрузку, как исключение пропадало. Тут взгляд упал на строку:
@EqualsAndHashCode(exclude = {"tags"})
которая подменяла стандартные методы equals и hashCode. Эта аннотация принадлежит великолепной библиотеке lombok, позволяющей при компиляции генерировать boilerplate код: toString, getter, setter, equalsAndHashCode.
Тем не менее, подменять хэшкод в мутабельных классах - очень плохая идея, ведущая к крайне веселым последствиям. Как оказалось, при персисте, гибернейт дергал метод equals, тот в свою очередь пробегался по полям и задевал ленивый прокси, прокси инициализировался и изменял хэш объекта, гибернейт видел это и сходил с ума от внезапного изменения уже обработанной коллекции.
На фикс бага ушло несколько часов. Самое веселое началось, когда мы открыли историю:
| 2013-02-19 | Удалено |
| 2012-11-28 | @EqualsAndHashCode(exclude = {"tags"}) |
| 2012-10-14 | @EqualsAndHashCode(exclude = {"users"}) |
| 2012-10-12 | @EqualsAndHashCode(exclude = {"users", "tags"}) |
| 2012-05-25 | @EqualsAndHashCode(exclude = {"users"}) |
| 2012-05-03 | @EqualsAndHashCode) |
А теперь, самое интересное, результаты раскопок: добавлен этот метод был для удобства тестирования. На тот момент, сущность не содержала коллекций и результаты тестов было очень удобно сравнивать с ожидаемыми объектами. Но спустя некоторое время, объект стал сложным и сравнение было переписано на toString, необходимость в подмене equals отпала. Тем не менее equals жил еще несколько месяцев, порождая трудноуловимые баги. А сколько еще таких привидений живет в коде?