I have an issue when calling multi-threads on my method, these threads can't all invoke the method at the same time, because I'm calling the method in a synchronized block, but each time a thread gets out of the block the next tread proceeds.
The method being invoked calls the em.find(Class<T> entityClass, Object primaryKey)
with the ID of my Entity(Binary), if the object exists in my database I update it and call merge()
for persistence purpose, and if it doesn't exist I create a new one with my id and call merge()
to save it on my database, in my case i'm calling all the threads with the same BinaryFile object and the id of this BinaryFile doesn't exist in my database.
the expected behavior is :
- The first thread that executes the method create the object and save it in the database.
- The remaining threads will not create a new
instance but just update the name field of my Binary object and call
merge()
to update in my database.
What really happens:
I get this exception :
org.hibernate.exception.ConstraintViolationException: ORA-00001: unique constraint (SCOM_DEV2.BINARY_ID_PK) violated
org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74)
org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
sun.proxy.$Proxy57.executeUpdate(Unknown Source)
org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:56)
org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2849)
org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3290)
org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:80)
org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:272)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:264)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:186)
org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1081)
org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:315)
org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion(SynchronizationCallbackCoordinatorImpl.java:104)
org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:53)
com.arjuna.ats.internal.jta.resources.arjun
Code Snippet of the invoked method
CreateOrUpdate(BinaryFile binaryFile){
Binary b = em.find(Binary.class, binarytmp.getId());
if(b==null){
b=new Binary();
b.setID(id);
}
b.setName(binarytmp.getName());
em.merge(b);
}
Code Snippet of the method's call
synchronized(binaryFile.getID().intern()){
CreateOrUpdate(binaryFile);
}
Code Snippet of my Entity
@Entity
@Table(name = "BINARY")
public class Binary implements ScoBinary {
/**
* id field.
*/
@Id
@Column(name = "id")
private String id;
/**
* name of the field
*/
@Column(name = "name")
private String name;
public String getID(){
return this.id;
}
public void setID(String){
return this.id = id;
}
public Stirng getName(){
return this.name;
}
public void setName(Stirng name){
return this.name=name;
}
}
When I call the same method with one thread, everything behave as expected, this exception is thrown only when I call the method with several threads, I tried to understand what's really happening, and i figured out that when the first thread executes the method, it creates the object and persist it, but the next thread gets a null when calling the find() method, although the first thread have already called merge() to save it in my database, i tried to call em.flush()
after my merge()
method to unsure the object is saved before calling find from the next thread but I still get the same result, can you please enlighten me.