I am getting a deadlock when performing the load testing with Soap UI
running 10 every thread makes 10 request to my web service which invokes the persits method of OpenJPA entity manager.
Then I am performing the single requests, all ends well, but in multithreaded environment it ends up with deadlocks.
The error I am getting is:
Deadlock found when trying to get lock; try restarting transaction {prepstmnt 12796448 UPDATE Assessment SET dateCompleted = ? WHERE id = ? [params=?, ?]} [code=1213, state=40001]
The method which is crashing with a deadlock is this:
@Override
public AssessmentKey update(AssessmentKey assessmentKey) {
OpenJPAEntityManager openJpaEntityMgr = OpenJPAPersistence.cast(entityManager);
for (Assessment assessment : assessmentKey.getAssessments()) {
if (!entityManager.contains(assessment) && !openJpaEntityMgr.isDetached(assessment)) {
LOGGER.debug("Persisting {}", assessment);
entityManager.persist(assessment);
}
}
return entityManager.merge(assessmentKey);
}
Assessment
is an entity of OpenJPA, it is just a normal entity containing JPA annotations.
If you'll need the code of it, ask for it.
I understood how deadlocks happening, how to solve them is mentioned in here. But how to solve them in ORM I am not able to find an answer.
My persistence.xml
file looks like this:
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="com.groupgti.esb.online.tests">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>com.groupgti.esb.assessments.model.jpaimpl.Assessment</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<validation-mode>NONE</validation-mode>
<properties>
<property name="openjpa.Log" value="slf4j"/>
<property name="openjpa.DataCache" value="false"/>
<property name="openjpa.ReadLockLevel" value="none"/>
<property name="openjpa.WriteLockLevel" value="pessimistic-write"/>
<property name="openjpa.LockTimeout" value="10000"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="unsupported"/>
</properties>
</persistence-unit>
</persistence>
Whole application uses Spring
and the classes in which transactions are done is marked @Transactional
. How to solve this deadlock issue in ORM?
EDIT:
The AssessmentKey
has a relation to Assessment
:
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, mappedBy = "assessmentKey")
private List<Assessment> assessments = new ArrayList<Assessment>();
And in the Assessment
AssessmentKey
looks like this:
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@Index
@Column(nullable = false)
@ForeignKey
private AssessmentKey assessmentKey;
AssessmentKey
and Assessment
are OneToMany
bidirectional relationship.Both of OneToMany
and ManyToOne
annotations have CascadeType.PERSIST
and others.
Point is :
when EntityManager
persist Assessment
, AssessmentKey
would persisted also. You should need to persist or merge AssessmentKey
again.
Let me suggest OR Mapping as follow :
AssessmentKey.java
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE},
mappedBy = "assessmentKey", orphanRemoval=true)
private List<Assessment> assessments;
Assessment.java
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.REFRESH})
@JoinColumn(name = "ASSESSMENT_ID", referencedColumnName = "ID")
private AssessmentKey assessmentKey;
DB Operation
@Override
public AssessmentKey update(AssessmentKey assessmentKey) {
OpenJPAEntityManager openJpaEntityMgr = OpenJPAPersistence.cast(entityManager);
// there may be your operation
return entityManager.merge(assessmentKey);
}
I assume, Assessment
values of current AssessmentKey
object may be as below
On System | In Existing Database
ID Description | ID Descrition
1001 AAA => No Changes | 1001 AAA
1002 BBB => Need to update | 1002 BB
null DDD => Need to add | 1003 CCC => need to remove.
When EntityManager merge AssessmentKey
, the EntityManager
will does the all of the process for Assessment
as I mention in above data structure.
Note : make sure orphanRemoval=true
in OneToMany
annotation.