2012-04-16 6 views
4

Использование Hibernate 4.0 У меня есть три Hibernate сущности:Дубликат ключевой вопрос с Hibernate

песни, CoverArt, CoverImage

Песни представляет музыкальный файл, CoverImage представляет собой изображение и CoverArt используется соотносить CoverImages композиций, песня может содержать несколько изображений обложки.

У песни и CoverArt есть первичный ключ, сгенерированный автоматически Hibernate. Но основной ключ Cover Image сделан вручную, созданный как MessageDigest данных изображения. Я делаю это, потому что одно и то же изображение может использоваться многими песнями, и я не хочу, чтобы отдельные экземпляры одного и того же изображения хранились несколько раз в базе данных, также потому, что ключ может быть создан из данных, которые я могу проверить в базе данных, существуют, и если они извлекают его, а не создают новый CoverImage.

Проблема заключается в том, что мое приложение многопоточно, а Hibernate фактически не передает вещи в базу данных сразу, поэтому нить 1 может проверить, действительно ли покровное изображение уже находится в базе данных, обнаружить, что оно не является и не создает новые объекты Song, CoverArt и CoverImage , Но к тому времени, получает данные фиксируются в базе данных CoverImage может быть дополнена отдельной нитью, так что я получаю исключение, потому что мой новый CoverImage имеет один и тот же ключ как существующий

Im используя

session.merge(coverImage); 

, так что я думал, что справится с этим, но, похоже, не помогает

+0

Добавить еще несколько фрагментов кода. –

ответ

4

Нет надежного способа справиться с этой ситуацией, кроме повторной попытки неудачной транзакции.

Итак, если вы получите откат транзакции из-за нарушения ограничений на первичный ключ CoverImage, вы должны повторить транзакцию, предполагая, что CoverImage уже существует. Обратите внимание, что для этого вам нужен новый Session, потому что исключение Hibernate невозвратимо.

merge() не может справиться с этой проблемой, поскольку ее причины лежат глубже, в семантике изоляции транзакций. В современных СУБД каждая транзакция видит свой собственный снимок базы данных. Таким образом, одновременные транзакции могут создавать конфликтующие изменения в их моментальных снимках (хотя они не могут вносить изменения в одну и ту же запись, поэтому эти изменения должны быть непересекающимися), и такой конфликт может быть обнаружен только СУБД во время фиксации и только если это вызывает ограничение нарушение, как в вашем случае (без конфликта ограничений будет незаметно, см. write skew anomaly).

С merge() работает внутри транзакции, он не видит, что делает другая транзакция в своих моментальных снимках, поэтому в этой ситуации невозможно преодолеть эту проблему.

+1

+1. Обратите внимание, что вы можете использовать очень короткую транзакцию только для getOrCreate CoverImage и повторить эту короткую транзакцию вместо повторной глобальной транзакции.Единственный недостаток заключается в том, что вы можете успешно создать CoverImage и выполнить глобальную транзакцию, что приведет к тому, что CoverImage не будет связан с каким-либо CoverArt, но я не думаю, что это будет проблемой. –

+0

Да, я думаю, что создание как отдельного txn только для добавления изображения обложки может иметь наибольший смысл, но я не понимаю, почему вызов merge() не обрабатывает проблему –

+0

@ijabz: Обновлено. – axtavt

1

Другой вариант - использовать обертку для coverImages. например. CoverImageWrapper. CoverImageWrapper имеет свой собственный ключ - uuId, отличный от messageDigest. Этот класс связан один с другим с CoverImage.

При хранении в базе данных эти ключи CoverImageWrapper всегда генерируются приложением, так что у вас есть все три клавиши (Song, CoverArt и CoverImageWrapper) - сгенерировано приложением, и оно будет уникальным во всей цепочке. Таким образом, вы можете избежать исключения дубликатов ключей.

+0

Но вы получите несколько строк Cover Image для того же изображения, что и вы? –

+0

Я думаю, вы можете иметь много к одному сопоставление между классами CoverImageWrapper и CoverImage. Hibernate позаботится о сохранении таких ассимиций. Там не будет дублирования экземпляров CoverImage. Вы должны переопределить equals и hashcode в классе coverImage. – zaga250

+0

хорошо, но я действительно стараюсь, чтобы все было просто, для меня это слишком сложная задача, спасибо в любом случае –

Смежные вопросы