Java JPA2:使用本机查询的死锁

Java JPA2:使用本机查询的死锁,java,mysql,multithreading,eclipselink,jpa-2.0,Java,Mysql,Multithreading,Eclipselink,Jpa 2.0,我正试图从表中取出记录。所以我使用以下本机查询自连接来获取。我在Windows上使用EclipseIDE、EclipseLink JPA2和MySQL select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " +

我正试图从表中取出记录。所以我使用以下本机查询自连接来获取。我在Windows上使用EclipseIDE、EclipseLink JPA2和MySQL

    select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + <offset> + "," + <iTotalRecords> + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp
下面是获取记录的方法

    public List<MyTable> fetchRecords(User theUser, int iTotalRecords, long offset) throws Exception {
        if(null == theUser) {
            throw new Exception("Invalid input.");
        }

        EntityManager entityManager = getEntityManager();
        if(false == entityManager.getTransaction().isActive()) {
            entityManager.getTransaction().begin();
        }

        try {
            Query query = entityManager.createNativeQuery("select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + offset + "," + iTotalRecords + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp", MyTable.class);
            @SuppressWarnings("unchecked")
            List<MyTable> resultList = query.getResultList();
            return resultList;
        } catch(Throwable th) {
            throw new Exception(th.getMessage());
        } finally {
            closeEntityManager();
        }
    }
我的persistence.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="JPATest">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

    <class>com.company.service.db.pojo.User</class>
    <class>com.company.service.db.pojo.MyTable</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/mydb"></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-or-extend-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="1000"/>
    <property name="eclipselink.target-database" value="MySQL"/>
    </properties>

    </persistence-unit>
    </persistence>
我的应用程序支持多线程

为了复制死锁,我运行了100个线程,每个线程执行fetchRecords方法。每次提取的记录约为200条或更多


请让我知道如何解决这个问题。非常感谢您的帮助。

根据您的描述,所有线程仅执行此只读查询。由于锁定而不是死锁,您可能会经历更长的时间。两者都是不同的东西,锁定本身不是问题,当然,除非它持续很长时间。锁定是数据库固有的功能,可提供一致的数据视图,并且有不同类型/级别的锁定。你可以阅读更多关于它的内容。粗略地看一下,您的SQL是一个常用的select,所以我看不出有任何理由也要锁定。有关锁的信息,请参阅 一旦确定这是需要时间的查询,就可以使用EXPLAIN来微调查询。这是一篇优秀的文章。也许适当的索引也会对您有所帮助

然而,为了消除您的疑虑,您已经有了一个很棒的工具,您可以在MySQL命令提示符下运行以下命令

SHOW ENGINE INNODB STATUS\g

这将为您提供有关死锁(如果有)的信息。

这是为遇到此问题的人提供的解决方案。JPA不会关闭连接,因为它已激活。这意味着我必须对代码进行注释才能开始事务

    public List<MyTable> fetchRecords(User theUser, int iTotalRecords, long offset) throws Exception {
        if(null == theUser) {
            throw new Exception("Invalid input.");
        }

        EntityManager entityManager = getEntityManager();
        //if(false == entityManager.getTransaction().isActive()) {
        //  entityManager.getTransaction().begin();
        //}

        try {
            Query query = entityManager.createNativeQuery("select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + offset + "," + iTotalRecords + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp", MyTable.class);
            @SuppressWarnings("unchecked")
            List<MyTable> resultList = query.getResultList();
            return resultList;
        } catch(Throwable th) {
            throw new Exception(th.getMessage());
        } finally {
            closeEntityManager();
        }
    }

根据上述信息,我可以猜测如下:

1.-正如您所指出的@User12111111,代码已经开始,但它缺少提交或释放事务的等价物。但是,由于您选择在解决方案中删除它,请确保您包含在托管服务器中。如果没有,还有更多:

2.-如果持久化单元JPATest在后续循环中被重用,则结果查询MyTable对象可能仍被管理。如果不需要更新生成的对象,则

3.-此外,当查询锁定太多行时,由于性能原因,锁会升级到hole表。这可能会在你认为不应该的地方造成死锁。如果您不需要行锁定,正如@Shailendra所指出的那样


4.-最后但并非最不重要的一点是,如果您不需要重复读取等,您可以简单地减少死锁的限制。

什么是症状,是什么让您认为这是JPA2死锁?我怀疑,但不是100%确定。您检查了什么?当您怀疑存在死锁时,最简单的方法就是对Java进程进行线程转储,并检查线程在等待什么。这应该给你一些提示,而不是在没有任何证据的情况下怀疑。@AdrianShum-从哪里获得java进程的线程转储?来自任务管理器?取决于您运行的平台。在windows中,点击controlbreak应该可以做到这一点。在Unix环境中,您可以通过kill-QUIT或kill-3发送一个QUIT信号来实现这一点。在JDK的更高版本中,它还包括一个名为jstack的工具,它可以通过jstack获取进程id的线程转储。我在MySQL上没有看到任何锁。我认为该线程在org.eclipse.persistence.sessions.server包的acquireConnection方法中等待数据库连接。未触发此releaseConnection连接。
SHOW ENGINE INNODB STATUS\g
    public List<MyTable> fetchRecords(User theUser, int iTotalRecords, long offset) throws Exception {
        if(null == theUser) {
            throw new Exception("Invalid input.");
        }

        EntityManager entityManager = getEntityManager();
        //if(false == entityManager.getTransaction().isActive()) {
        //  entityManager.getTransaction().begin();
        //}

        try {
            Query query = entityManager.createNativeQuery("select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + offset + "," + iTotalRecords + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp", MyTable.class);
            @SuppressWarnings("unchecked")
            List<MyTable> resultList = query.getResultList();
            return resultList;
        } catch(Throwable th) {
            throw new Exception(th.getMessage());
        } finally {
            closeEntityManager();
        }
    }