如何在不同线程中调用 SQL 更新后修复 'SQLException SQL0913'

How to fix 'SQLException SQL0913' after calling SQL update in different threads

我正在调用一种方法,该方法对 DB2 数据库中的单个数据集执行 SQL 更新。在收到来自 ActiveMQ 队列的消息后,该方法会在不同的线程中自动调用。它在 2 秒内被调用了 5 次,但是因为第一次调用在数据集上设置了 WRITE-Lock,接下来的 3 次调用必须等待。第一次调用完成更新后,第二次调用不执行更新,而是运行新的方法调用(行中的第 5 次调用)。问题是,中间的三个调用没有识别,WRITE-Lock 被释放,并在 1 分钟超时后抛出错误 SQL0913 的 SQLException。

Caused by: java.sql.SQLException: [SQL0913] Zeile oder Objekt PR1BAFPU5 der Art *FILE in DAKDTA wird verwendet. Ursache  . . . . :  Das angeforderte Objekt PR1BAFPU5 der Art *FILE in der Bibliothek DAKDTA wird gerade von einem anderen Anwendungsprozess verwendet, oder eine Zeile im Objekt wird von einem anderen Anwendungsprozess oder einem anderen Cursor in diesem Anwendungsprozess verwendet. Fehlerbeseitigung:  Die vorherigen Nachrichten im Jobprotokoll aufrufen (Befehl DSPJOBLOG) oder im interaktiven SQL F10 (Nachrichten im Jobprotokoll anzeigen) in dieser Anzeige drücken, um zu bestimmen, ob es sich um eine Wartezeitüberschreitung für eine Objekt- oder Satzsperre handelt. Einen der folgenden Schritte durchführen: -- Wird ein Objekt durch einen anderen Anwendungsprozess gesperrt, die SQL-Anweisung wiederholen, wenn das Objekt nicht verwendet wird. Mit dem Befehl WRKOBJLCK (Mit Objektsperren arbeiten) kann festgestellt werden, von wem das Objekt gerade verwendet wird. -- Ist das Objekt ein Schema und wurde versucht, in diesem Schema eine Tabelle, eine Sicht oder einen Index unter COMMIT-Steuerung zu erstellen, wird möglicherweise für dieses Schema gerade eine Operation zum "Sichern im aktiven Zustand" von einem anderen Job im System durchgeführt. Ist die Operation zum "Sichern im aktiven Zustand" abgeschlossen, die Anforderung wiederholen. -- Wird ein Satz durch einen anderen Anwendungsprozess gesperrt, die SQL-Anweisung wiederholen, wenn der Satz nicht verwendet wird. Mit dem Befehl DSPRCDLCK (Satzsperren anzeigen)kann festgestellt werden, von wem der Satz gerade verwendet wird. -- Wird der Satz von einem anderen Cursor in demselben Anwendungsprozess gesperrt, muss eine Anweisung COMMIT, ROLLBACK oder eine andere Anweisung FETCH für den Cursor ausgegeben werden, der die Sperre verursacht, bevor diese SQL-Anweisung ausgegeben wird. Tritt dieser Fehler häufig auf, mit dem Befehl CHGPF (Physische Datei ändern), CHGLF (Logische Datei ändern) oder OVRDBF (Datenbankdatei überschreiben) die Wartezeitüberschreitung für das Objekt oder den Satz ändern. 
        at com.ibm.as400.access.JDError.createSQLExceptionSubClass(JDError.java:860) [jt400-jdbc4-7.8.jar:JTOpen 7.8] 
        at com.ibm.as400.access.JDError.throwSQLException(JDError.java:692) [jt400-jdbc4-7.8.jar:JTOpen 7.8] 
        at com.ibm.as400.access.JDError.throwSQLException(JDError.java:662) [jt400-jdbc4-7.8.jar:JTOpen 7.8] 
        at com.ibm.as400.access.AS400JDBCStatement.commonExecute(AS400JDBCStatement.java:1025) [jt400-jdbc4-7.8.jar:JTOpen 7.8] 
        at com.ibm.as400.access.AS400JDBCPreparedStatement.executeUpdate(AS400JDBCPreparedStatement.java:1649) [jt400-jdbc4-7.8.jar:JTOpen 7.8] 
        at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:493) 
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:186) [hibernate-core-4.2.27.Final-redhat-1.jar:4.2.27.Final-redhat-1] 
        ... 143 more 

数据库是 IBM DB2 数据库(7.2 版),我的程序是 运行 在 JBoss EAP-6.4 上。从日志来看,第一个调用似乎需要几秒钟才能执行,在接下来的三个调用之间正在等待第一个调用。但是在第一个执行完后,接下来的三个调用没有进行。

public void updateBerthTimeConflictFlag4BerthArrival(boolean conflict, String berthArrivalId) { 
                Query updateQuery = em.createNativeQuery("update PR1BERTHARRIVAL_FLZ set berthTimeConflict = :conflict where id = :id"); 
                updateQuery.setParameter("conflict", conflict ? 1 : 0); 
                updateQuery.setParameter("id", berthArrivalId); 
                updateQuery.executeUpdate(); 
        } 

我预计,在第一次调用完成后,SQL 更新的接下来的三个调用将按调用顺序执行。

我们之前遇到过同样的问题,我们按照这个:

问题(摘要)

当应用程序使用 JDBC 从 table 中读取多条记录并使用 BMP 实体 bean 从 table 中删除记录时,错误 "java.sql.SQLException: [SQL0913] Row or object F55500 in type *FILE in use" 发生。

原因

异常表示行或对象正在使用中。

解决问题

在读取多条记录的JDBC程序中,需要保证关闭所有连接,释放所有资源。 如果没有直接释放连接的方法,重新启动数据库将删除所有打开的连接,但如果没有其他方法可以释放连接,这将是最后的措施。 不使用 JDBC 程序,而是创建一个单独的会话 bean 来包含对数据库的查询。会话 bean 的事务设置应为 RequiresNew。 SQL 语句现在在新的单独事务中运行。方法完成后 运行,事务结束,table 被释放以供 BMP 实体 bean 用于删除。

这里是原文technote