java - JPA 2.0 如何处理死锁(Eclipselink JPA2.0 MySQL)

标签 java mysql jpa jpa-2.0 eclipselink

我正在尝试添加/获取/更新代码,并且我使用了 EclipseLink 提供程序和 JPA2.0。和MySQL。

下面的代码抛出一个错误,指出发生了死锁。问题是随机发生的。我想知道如何处理死锁。

这是错误信息:

    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)

这是我正在尝试的代码:

    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();
            }
        }
    }

这是实体类

    @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;
        }

    }

这是我的 DAO 类:

    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;
        }
    }

这是我的 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>

当 AcitivityDAO 尝试更新时,会发生死锁。为什么要处理或避免死锁问题?

感谢任何帮助!!


我收到以下错误:

      javax.persistence.PersistenceException: java.lang.NullPointerException

      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 = ?)")

我使用了@Chris Ridal 指定的相同代码。

这是代码:基本上我尝试多次运行 MainTest 类。

    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;
        }

    }

是否必须为每个数据库插入、合并、更新或删除获取 EntityManager?看下面的代码,我没有看到死锁发生。请确认。

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;
    }
}

最佳答案

一般you do not need to use DAO's when using JPA .

相反,您可能希望使用这样的类(未经测试),带来您自己的 EntityManagerFactory:

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);
    }

    // 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();
    }

}

要使用它,您可以在创建对象时调用 persist(entityObj),调用 flush() 与数据库同步(如果需要)和commitAndClose() 完成后。将 PersistenceController 放在您需要保留对象或使用其其他操作时可以发布到它的地方。

现在您的事务不会并发发生,您也不会遇到死锁。

注意:在生产代码中,您将使用更多的异常管理并将您的工作拆分为不同的 EntityManager 事务,如果您在逻辑上配置和创建此 PersistenceController 类,则此类会为您执行此操作。

关于java - JPA 2.0 如何处理死锁(Eclipselink JPA2.0 MySQL),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20074583/

相关文章:

java - ANTLR 不匹配输入 'foo(some_foo)' 期望 {'foo' }

java - java 是否有一个 Util 可以从列表中创建一个分隔字符串,并将每个值包装在某个字符中?

java - 合并两个 WebElements 列表

MYSQL - 选择所有行,但在一列上使用 Distinct

java - hibernate 和日期类型 : length attribute

java - @AssociationOverride 和 @joinColumn 不起作用

java - 元音计数、重复法

mysql - 如何合并 3 个查询?

php - 结合两个 ajax 调用 - 在 url 中存储多个 post 变量

java - 如果我使用 Java 中的 JPA 搜索 child 的 parent 之一,如何获得 child 的姓名作为结果?