Я использую Hibernate с присоединенными подклассами для сопоставления иерархии классов с базой данных. К сожалению, это вызывает взаимоблокировки, когда объект обновляется, когда другой поток пытается загрузить тот же объект. С объектами, сопоставленными с одной таблицей, это не проблема. По-видимому, это вызвано тем, как MSSQL получает блокировки для таблиц иерархии классов.
Когда Hibernate загружает объект из базы данных, он использует SELECT с JOIN:
SELECT ...
FROM
subclass
LEFT JOIN class
ON ...
WHERE ...
Когда Hibernate обновляет объект этого подкласса, он делает:
UPDATE
class
SET ...
WHERE ...
UPDATE
subclass
SET ...
WHERE ...
Проблема в том, что если объект загружается между двумя операторами обновления, это вызывает взаимоблокировку. Оператор SELECT блокирует 2 таблицы одну за другой. Итак, что, кажется, происходит:
- Поток 1 загружает объект и устанавливает общие блокировки на обе таблицы.
- Поток 1 выполняет оператор UPDATE для таблицы классов и обновляет блокировку таблицы классов до монопольной блокировки.
- Поток 2 пытается загрузить тот же объект, выполняя оператор SELECT, он устанавливает общую блокировку на таблицу подклассов, а затем ждет, пока монопольная блокировка таблицы класса не будет снята.
- Поток 1 выполняет оператор UPDATE для таблицы подклассов, он хочет обновить свою блокировку таблицы подклассов до эксклюзивной блокировки, но таблица уже заблокирована потоком 2, ожидающим потока 1.
- Поток 2 прерван из-за взаимоблокировки с потоком 1.
График взаимоблокировок выглядит следующим образом: График взаимоблокировок
Эти объекты часто обновляются тихо, и это постоянно вызывает взаимоблокировки, даже когда загружается только один объект. Я также пытался воспроизвести проблему с HSQLDB, но тогда она не блокируется, кажется, что HSQLDB либо блокирует обе таблицы одновременно, либо ждет, пока не сможет заблокировать обе, так что эта проблема возникает только с MSSQL.
Каким было бы решение, чтобы избежать этой проблемы с Hibernate без изменения схемы (кроме индексов)?