4

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

0

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

6 Comments

+1. Note that you could use a very short transaction just to getOrCreate the CoverImage, and retry this short transaction, instead of retrying the whole, global transaction. The only drawback is that you could create a CoverImage successfully and have the global transaction fail, which would lead to a CoverImage not linked to any CoverArt, but I don't think this would be a problem.
Yes I think creating as seperate txn just for adding cover image might make most sense, but I dont understand why the call to merge() doesnt handle the problem
Thankyou, Understand ( I think), but now Im wondering if my application design is incorrect, or is this kind of problem to be expected
@ijabz: It's an expected problem.
If I delegated creation of CoverImage to a single threaded Executor Service then that should avoid it shouldn't it
|
1

Another option is to use wrapper for coverImages. for eg. CoverImageWrapper. CoverImageWrapper has its own key - uuId based, other than messageDigest. This class is linked one to one with CoverImage.

In storing into database this CoverImageWrapper keys are always generated by application, so this way you have all the three keys (Song, CoverArt and CoverImageWrapper) - generated by application and it will be unique across all the thread. So, this way you can avoid duplicate keys exception.

3 Comments

But you'll end up with multiple Cover Image rows for the same Image wont you ?
I think, you can have many to one mapping between CoverImageWrapper and CoverImage classes. Hibernate will take care while storing such assiciations. There wont be duplication of CoverImage instances. You need to override equals and hashcode in coverImage class.
okay, but Im really trying to keep things simple, this is too much complexity for me, thanks anyway

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.