¿Por qué hibernate da la excepción ConstraintException como si intentara crear un objeto cuando ya existe uno?

Estoy usando saveOrUpdate () al actualizar / crear un objeto CoverImage. Muy ocasionalmente obtengo una infracción de restricción en la clave principal.

org.hibernate.exception.ConstraintViolationException: 
Unique index or primary key violation: "PRIMARY_KEY_6 
ON PUBLIC.COVERIMAGE(DATAKEY)"; SQL statement:

Parece que está intentando crear una nueva CoverImage (INSERT) en lugar de actualizar una CoverImage (UPDATE) existente, pero no sé por qué, ya que datakey se define como el @id de la clase y estoy configurando la datakey.

Utilizo saveOrUpdate () en lugar de tener partes separadas de save () y update () porque el código es multiproceso. Verifico la existencia de la instancia antes de llamar a este método y solo llamo si el objeto no existe, por lo que no espero que ya exista, pero siempre existe la posibilidad, como en el caso. El problema parece ocurrir aproximadamente 1 de cada 3000 veces.

Aquí está la clase de hibernación.

package com.jthink.songlayer;

import com.jthink.songlayer.utils.Base64Coder;
import org.hibernate.annotations.IndexColumn;
import org.hibernate.envers.Audited;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
import javax.persistence.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *  An Image
 */
@Audited
@Entity
public class CoverImage
{

    public CoverImage()
    {

    }

    public CoverImage(byte[] imageData)
    {
        this.imageData=imageData;
    }

    @Id
    @Column(length = 1000)
    private String dataKey;

    @Version
    private int version;

    public String getDataKey()
    {
        return dataKey;
    }

    public void setDataKey(String dataKey)
    {
        this.dataKey = dataKey;
    }

    @Lob
    private byte[]  imageData;

    @Lob
    private byte[]  thumbnailData;

    private String  mimeType;
    private int     width;
    private int     height;
    private boolean isLinked;

    @org.hibernate.annotations.Index(name = "IDX_SOURCE")
    private String  source;

    @Lob
    private byte[]  resizedImageData;
    private int     resizedWidth;
    private int     resizedHeight;

    public byte[] getImageData()
    {
        return imageData;
    }

    public void setImageData(byte[] imageData)
    {
        this.imageData = imageData;
    }

    public byte[] getThumbnailData()
    {
        return thumbnailData;
    }

    public void setThumbnailData(byte[] thumbnailData)
    {
        this.thumbnailData = thumbnailData;
    }

    public String getMimeType()
    {
        return mimeType;
    }

    public void setMimeType(String mimeType)
    {
        this.mimeType = mimeType;
    }

    public int getWidth()
    {
        return width;
    }

    public void setWidth(int width)
    {
        this.width = width;
    }

    public int getHeight()
    {
        return height;
    }

    public void setHeight(int height)
    {
        this.height = height;
    }

    public boolean isLinked()
    {
        return isLinked;
    }

    public void setLinked(boolean linked)
    {
        isLinked = linked;
    }


    public String getSource()
    {
        return source;
    }

    public void setSource(String source)
    {
        this.source = source;
    }

    public byte[] getResizedImageData()
    {
        return resizedImageData;
    }

    public void setResizedImageData(byte[] resizedImageData)
    {
        this.resizedImageData = resizedImageData;
    }

    public int getResizedWidth()
    {
        return resizedWidth;
    }

    public void setResizedWidth(int resizedWidth)
    {
        this.resizedWidth = resizedWidth;
    }

    public int getResizedHeight()
    {
        return resizedHeight;
    }

    public void setResizedHeight(int resizedHeight)
    {
        this.resizedHeight = resizedHeight;
    }

    /**
     * Create message digest of the byte data
     * <p/>
     * This uniquely identifies the imagedata, but takes up much less room than the original data
     *
     * @param imageData
     * @return
     */
    public static byte[] getImageDataDigest(byte[] imageData)
    {
        //Calculate checksum
        MessageDigest md;
        try
        {
            md = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException nsae)
        {
            //This should never happen
            throw new RuntimeException(nsae);
        }

        md.reset();
        md.update(imageData);
        return md.digest();
    }

    public static String createKeyFromData(byte[] imageData)
    {
        try
        {
            String base64key = CharBuffer.wrap(Base64Coder.encode(getImageDataDigest(imageData))).toString();
            return base64key;
        }
        catch (NullPointerException npe)
        {
            throw new RuntimeException("Unable to create filename from sum");
        }
    }
}

Y este es el código que lo usa.

   try
    {
        //Create thumbnail
        BufferedImage           thumb = ArtworkHelper.resizeToThumbnail(newBuffered, THUMBNAIL_SIZE);
        ByteArrayOutputStream   baos    = new ByteArrayOutputStream();
        ImageIO.write(thumb, ImageFormats.V22_JPG_FORMAT.toLowerCase(), baos);
        session = com.jthink.songlayer.hibernate.HibernateUtil.getSession();
        Transaction tx  = session.beginTransaction();
        coverImage = new CoverImage(imageData);
        coverImage.setThumbnailData(baos.toByteArray());
        coverImage.setDataKey(CoverImage.createKeyFromData(imageData));
        coverImage.setSource(source);
        coverImage.setWidth(newBuffered.getWidth());
        coverImage.setHeight(newBuffered.getHeight());
        coverImage.setMimeType(ImageFormats.getMimeTypeForBinarySignature(imageData));
        session.saveOrUpdate(coverImage);
        tx.commit();
        return coverImage;
    }
    catch(IOException ioe)
    {
        MainWindow.logger.log(Level.SEVERE, "Failed Creating Thumbnails" + ioe.getMessage(), ioe);
        return null;
    }
    catch(StaleObjectStateException sose)
    {
        return SongCache.findCoverImageBySourceInOwnSession(source);
    }
    finally
    {
        HibernateUtil.closeSession(session);
    }

Traza de pila completa

10/01/2013 09.17.12:com.jthink.songkong.analyse.analyser.DiscogsSongGroupMatcher:call:SEVERE: Failed AddSongToDatabase:Unique index or primary key violation: "PRIMARY_KEY_6 ON PUBLIC.COVERIMAGE(DATAKEY)"; SQL statement:
insert into CoverImage (height, imageData, isLinked, mimeType, resizedHeight, resizedImageData, resizedWidth, source, version, width, dataKey) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-166]
org.hibernate.exception.ConstraintViolationException: Unique index or primary key violation: "PRIMARY_KEY_6 ON PUBLIC.COVERIMAGE(DATAKEY)"; SQL statement:
insert into CoverImage (height, imageData, isLinked, mimeType, resizedHeight, resizedImageData, resizedWidth, source, version, width, dataKey) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-166]
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:128)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
at $Proxy27.executeUpdate(Unknown Source)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:56)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2962)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3403)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:88)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:275)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1210)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:399)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.jthink.songkong.db.SongCache.saveNewCoverImage(SongCache.java:332)

Respuestas a la pregunta(2)

Su respuesta a la pregunta