在多线程环境中使用 select 进行更新时,只读结果集的操作无效

Invalid operation for read only resultset when using select for update nowait in multithreaded environment

对于 Oracle 数据库,以下程序将仅针对某些线程抛出 SQL 异常。为什么要将 resultSetConcurrency 从 CONCUR_UPDATABLE to CONCUR_READ_ONLY 降级?在单线程环境中,这不会发生。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


public class Main extends Thread {

    public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:DB";
    public static final String DBUSER = "USER";
    public static final String DBPASS = "PASS";

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        for(int i=0; i<20; i++)
            new Main().start();
    }

    @Override
    public void run() {

        try
        {
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

            Connection con = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
            con.setAutoCommit(false);

            try(PreparedStatement pstmt = con.prepareStatement("SELECT COLUMN1 FROM TABLE1 FOR UPDATE NOWAIT", 
                    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE))
            {               
                ResultSet rs = pstmt.executeQuery();

                if (rs.next()) {
                    rs.updateString(1, "12345");
                    rs.updateRow();
                }            
            }
            finally
            {
                con.commit();
                con.close();
            }
        }
        catch(SQLException e)
        {
            if(!e.toString().contains("NOWAIT"))
                e.printStackTrace();
        }
    }

}

您可以查看针对结果 set/statement/connection 提出的警告,了解它被降级的原因。在 executeQuery() 调用之后添加:

SQLWarning warning = pstmt.getWarnings();
while (warning != null)
{
    System.out.println("Warning: " + warning.getSQLState()
        + ": " + warning.getErrorCode());
    System.out.println(warning.getMessage());
    warning = warning.getNextWarning();
}

在这种情况下,您有时会看到:

Warning: 99999: 17091
Warning: Unable to create resultset at the requested type and/or concurrency level: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

您正在查找 NOWAIT 异常,但收到警告。我不清楚的是为什么在那种情况下你仍然得到一个结果集;但你至少可以捕获该警告,如果你看到它,则不要进入结果集循环。