У нас возникла эта проблема при переносе системы транзакций, поддерживаемой MySQL, в базу данных H2.(Несколько похоже на эту проблему на устаревших форумах Hibernate.)
Наша история
- У нас были модели объектов с полями BLOB-объектов.
- В коде мы транзакционно извлекали пакеты этих сущностей, обновляли их статус до «обработки», а затем считывали полезные данные больших двоичных объектов за пределами транзакции (после подтверждения обновления статуса) для фактической обработки.
- Изначально отложенная загрузка не работала, поэтому каждая выборка извлекала содержимое поля большого двоичного объекта вместе с сущностью.
При фиксации транзакции Hibernate проверяет грязность каждого поля и отправляет обновления для всех грязныхПоля обратно в БД.
В механизме обнаружения изменений Hibernate к двоичным объектам всегда относились как к грязным (возможно, из-за того, что вы не можете сравнивать два больших объекта неинвазивным способом; например, если один большой объект имеет потоковую поддержку, выпришлось бы фактически использовать поток, чтобы сравнить его содержимое с другим BLOB-объектом).
Проверьте:
org.hibernate.type.AbstractStandardBasicType#isDirty(java.lang.Object, java.lang.Object, boolean[], org.hibernate.engine.spi.SharedSessionContractImplementor)
и org.hibernate.type.descriptor.java.BlobTypeDescriptor#areEqual
, чтобы увидеть, как сравнение двоичных объектов в конечном итоге сводится к простой проверке ==
.
Хотя вы можете ожидать, что это все равно вернет true (поскольку мы никогда не касаемся поля blob), обтекание прокси и другие внутренние объекты уровня Hibernate приведут к тому, что конечный объект blob будет вообще другим объектом, что не пройдёт тест ==
.
В результате ...
На этапе фиксации каждый ранее извлеченный большой двоичный объект использовался (считывался) для записи его содержимого в базу данных.
Это не проблема для MySQL, поскольку ихBLOB-объекты находятся в памяти (и могут быть прочитаны несколько раз, не вызывая исчерпание потока).Однако H2 предоставляет потоковые двоичные объекты, что означает, что они доступны только для чтения.
Итак, когда мы пытались (повторно) прочитать большой двоичный объект после транзакции, Hibernate пытался сбросить уже использованный поток для следующего чтения, и это не помогло.
Наше решение
Решением было просто включить отложенную загрузку для полей BLOB-объектов , как указано в в этом ответе SO .