使用Hibernate 4.0,我有三个休眠实体:
歌曲,CoverArt,CoverImage
歌曲表示音乐文件,CoverImage表示图像,CoverArt用于将CoverImages与歌曲相关联,一首歌曲可以包含多个封面图像。
Song和CoverArt具有由Hibernate自动生成的主键。但是Cover Image主键是手动完成的,构造为图像数据的MessageDigest。我这样做是因为同一首图像可以被许多歌曲使用,并且我不希望同一首图像的单独实例多次存储在数据库中,也因为可以根据数据构造密钥,所以我可以在数据库中检查文件是否已经存在存在,并且如果存在,则检索它,而不是构造一个新的CoverImage。
问题是我的应用程序是多线程的,并且Hibernate实际上并没有立即将事情提交给数据库,因此线程1可能会检查Coverimage是否已经在数据库中,发现它不是并构造一个新的Song,CoverArt和CoverImage对象。但是,当数据提交到数据库时,一个CoverImage可能已经由一个单独的线程添加了,所以我得到了一个异常,因为我的新CoverImage具有相同的密钥
作为一个现有的
我正在使用
session.merge(coverImage);
所以我认为可以解决这个问题,但似乎无济于事
最佳答案
除了重试失败的事务外,没有可靠的方法来处理这种情况。
因此,如果由于对CoverImage
主键的约束违反而导致事务回滚,则应假定CoverImage
已经存在,然后重试事务。请注意,您需要一个新的Session
来执行此操作,因为Hibernate异常是无法恢复的。merge()
无法处理此问题,因为在事务隔离语义中,其原因更深。在基于MVCC的现代DBMS中,每个事务都会看到其自己的数据库快照。因此,并发事务可以对其快照进行冲突的更改(尽管它们不能对同一记录进行更改,因此这些更改必须是不相交的),并且只有在提交引起约束的情况下,DBMS才能检测到这种冲突。违反情况,例如您的情况(不会引起约束冲突,请注意write skew anomaly)。
由于merge()
在事务内部工作,因此它无法在快照中看到其他事务做什么,因此无法克服此问题。