I trying a code to add/get/update and I have used EclipseLink provider and JPA2.0. and MySQL.
The below code is throwing an error saying a deadlock happened. The issue is happening randomly. I wanted to know how to handle deadlock.
Here is the error message:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: null, message from server: "Deadlock found when trying to get lock; try restarting transaction"
Error Code: 1213
Call: UPDATE activitylog SET timestampdate = ? WHERE (logid = ?)
bind => [2013-11-19 20:10:38.583, 1]
Query: UpdateObjectQuery(test.ActivityLog@dd3314)
Here is the code that I am trying:
public class TestMain {
public static void main(String[] args) {
for(int j = 0; j < 10; j ++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
ActivityLogDAO activityLogDAO = new ActivityLogDAO();
try {
ActivityLog theActivityLog = new ActivityLog();
theActivityLog.setTimestampdate(new Date());
theActivityLog.setMyId(i);
activityLogDAO.insert(theActivityLog);
ActivityLog activityLog = activityLogDAO.getActivityLog(theActivityLog);
activityLog.setTimestampdate(new Date());
activityLogDAO.update(activityLog);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
}
}
Here is the Entity class
@Entity
@Table(name="activitylog")
public class ActivityLog implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name="logid")
private long logid;
@Column(name="myid")
private long lMyId;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="timestampdate", nullable=true)
private Date timestampdate;
public long getMyId() {
return lMyId;
}
public void setMyId(long lMyId) {
this.lMyId = lMyId;
}
public long getLogid() {
return logid;
}
public void setLogid(long logid) {
this.logid = logid;
}
public Date getTimestampdate() {
return timestampdate;
}
public void setTimestampdate(Date timestampdate) {
this.timestampdate = timestampdate;
}
}
here is my DAO class:
public class ActivityLogDAO {
private EntityManagerFactory _entityManagerFactory = null;
private EntityManager _entityManager = null;
public ActivityLogDAO() {
_entityManagerFactory = Persistence.createEntityManagerFactory("MyTestOnLock");
_entityManager = _entityManagerFactory.createEntityManager();
}
protected EntityManager getEntityManager() {
return _entityManager;
}
protected void setEntityManager(EntityManager _entityManager) {
this._entityManager = _entityManager;
}
public ActivityLog insert(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("inserting");
getEntityManager().persist(theActivityLog);
getEntityManager().getTransaction().commit();
System.out.println("inserted");
return theActivityLog;
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to get object");
Query query = getEntityManager().createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
//deadlock happens here.
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to update object");
Query query = getEntityManager().createQuery("UPDATE ActivityLog m SET m.timestampdate = :timestampdate WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
query.setParameter("timestampdate", theActivityLog.getTimestampdate());
int executeUpdate = query.executeUpdate();
getEntityManager().getTransaction().commit();
System.out.println("object updted.");
if(0 == executeUpdate) {
return null;
}
return theActivityLog;
}
}
Here is my persistance.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="MyTestOnLock">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>test.ActivityLog</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/locktest"></property>
<property name="javax.persistence.jdbc.user" value="root"></property>
<property name="javax.persistence.jdbc.password" value="root"></property>
<!-- EclipseLink should create the database schema automatically -->
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
<property name="eclipselink.id-validation" value="NULL"></property>
<property name="eclipselink.logging.level" value="FINE"/>
<property name="javax.persistence.lock.timeout" value="100"/>
<property name="eclipselink.order-updates" value="true"/>
<property name="eclipselink.connection-pool.sequence" value="max" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
<property name="eclipselink.target-database" value="MySQL" />
</properties>
</persistence-unit>
</persistence>
The deadlock occurs when AcitivityDAO is trying updated. Is there a why to handle or avoid deadlock issue?
Any help is appreciated!!
I am getting back the following error:
javax.persistence.PersistenceException: java.lang.NullPointerException
and
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Deadlock found when trying to get lock; Try restarting transaction, message from server: "Lock wait timeout exceeded; try restarting transaction"
Error Code: 1205
Call: UPDATE activitylog SET timestampdate = ? WHERE (myid = ?)
bind => [2013-11-20 16:54:09.646, 0]
Query: UpdateAllQuery(referenceClass=ActivityLog sql="UPDATE activitylog SET timestampdate = ? WHERE (myid = ?)")
I had used the same code which @Chris Ridal specified.
here is the code: Basically I tried running the MainTest class multiple times.
public class MainTest {
public static void main(String[] args) {
updateActivityLog();
}
private static void updateActivityLog() {
final PersistenceController persistenceController = new PersistenceController(Persistence.createEntityManagerFactory("MyTestOnLock"));
for (int i = 0; i < 100; i++) {
try {
for(int j = 0; j < 200; j++) {
ActivityLog theActivityLog = new ActivityLog();
theActivityLog.setMyId(j);
theActivityLog.setTimestampdate(new Date());
persistenceController.update(theActivityLog);
}
} catch (Exception e) {
e.printStackTrace();
}
}
persistenceController.commitAndClose();
}
}
public class PersistenceController {
private EntityManager manager;
public PersistenceController(EntityManagerFactory factory)
{
/*
* Normally you want to split your work up into separate transactions
* (ie new entity managers), in a logical way which will depend on how
* your application works. This class will do that for you if you keep
* your factory. Note that factory's are expensive to create but entity
* managers are cheap to create.
*/
manager = factory.createEntityManager();
manager.getTransaction().begin();
}
// Call ONCE on an object after creating it, it will stay in sync with the database even when you change it remotely
public void persist(Serializable entityObj)
{
manager.persist(entityObj);
manager.flush();
}
// Call to sync with database (even though you might not actually see the objects in the database until you commit)
public void flush()
{
manager.flush();
}
/*
* Call when you are done with your unit of work to commit the DB changes
*/
public void commitAndClose()
{
manager.getTransaction().commit();
manager.close();
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == manager.getTransaction().isActive()) {
manager.getTransaction().begin();
}
System.out.println("trying to get object");
Query query = manager.createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == manager.getTransaction().isActive()) {
manager.getTransaction().begin();
}
ActivityLog activityLog = getActivityLog(theActivityLog);
activityLog.setTimestampdate(theActivityLog.getTimestampdate());
persist(activityLog);
return theActivityLog;
}
}
Do I have to get EntityManager for every database insert or merge or update or delete? see below code, with this I am not seeing deadlock happening. Please confirm.
public class ActivityLogDAO {
private EntityManagerFactory _entityManagerFactory = null;
private EntityManager _entityManager = null;
public ActivityLogDAO() {
_entityManagerFactory = Persistence.createEntityManagerFactory("MyTestOnLock");
}
protected EntityManager getEntityManager() {
return _entityManager;
}
protected void setEntityManager(EntityManager _entityManager) {
this._entityManager = _entityManager;
}
public ActivityLog insert(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("inserting");
getEntityManager().persist(theActivityLog);
getEntityManager().getTransaction().commit();
System.out.println("inserted");
return theActivityLog;
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to get object");
Query query = getEntityManager().createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
//deadlock happens here.
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to update object");
Query query = getEntityManager().createQuery("UPDATE ActivityLog m SET m.timestampdate = :timestampdate WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
query.setParameter("timestampdate", theActivityLog.getTimestampdate());
int executeUpdate = query.executeUpdate();
getEntityManager().getTransaction().commit();
System.out.println("object updted.");
if(0 == executeUpdate) {
return null;
}
return theActivityLog;
}
}