在多线程环境中使用 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 异常,但收到警告。我不清楚的是为什么在那种情况下你仍然得到一个结果集;但你至少可以捕获该警告,如果你看到它,则不要进入结果集循环。
对于 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 异常,但收到警告。我不清楚的是为什么在那种情况下你仍然得到一个结果集;但你至少可以捕获该警告,如果你看到它,则不要进入结果集循环。