Duplicate key issue with Hibernate

hibernate, java

Solution

There is no reliable way to handle this situation except for retrying the failed transaction.

So, if you get transaction rollback due to constraint violation on primary key of `CoverImage` you should retry the transaction assuming that `CoverImage` already exists. Note that you need a new `Session` to do it, because Hibernate exceptions are irrecoverable.

`merge()` cannot handle this issue because its causes lie deeper, in transaction isolation semantics. In modern MVCC-based DBMSes each transaction sees its own snapshot of the database. So, concurrent transactions can make conflicting changes to their snapshots (though they cannot make changes to the same record, so that these changes must be disjoint), and such a conflict can only be detected by DBMS during commit, and only if it causes constraint violation, as in your case (without a constraint conflict will be unnoticed, see write skew anomaly).

Since `merge()` works inside transaction it cannot see what other transaction do in their snapshots, therefore in cannot overcome this problem.

Problem

Using Hibernate 4.0 I have three hibernate entities: Song, CoverArt, CoverImage Songs represents music file, CoverImage represents an image and CoverArt is used to relate CoverImages to Songs, a song can contain multiple cover images. Song and CoverArt have a primary key generated automatically by Hibernate. But Cover Image primary key done manually, constructed as a MessageDigest of the image data. I do this because the same image can be used by many songs and I dont want seperate instances of the same image stored multiple times in the database, also because the key can be constructed from the data I can check in the database whether the file already exist and if so retrieve it rather than constructing a new CoverImage. The trouble is my application is multithreaded and Hibernate doesnt actually commit things to the database immediaely, so thread 1 may check if the coverimage is already in the database, find that it isnt and construct a new Song, CoverArt and CoverImage objects. But by the time the data gets committed to the database a CoverImage may have been added by a seperate thread so I get an exception because my new CoverImage has the same key as an existing one Im using ``` session.merge(coverImage); ``` so I thought that would handle this, but it doesn't seem to help

Original source