首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

JDBC ResultSet CONCUR_UPDATABLE 報錯「read only」:原因排查與解決方案

     編輯:程式設計綜合問答

使用`ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE`創建可更新結果集,卻在`updateXxx`時報`ResultSet is read only`,通常由SQL查詢不滿足可更新條件或數據庫驅動不支持導致。本文系統排查原因並給出解決方案。

ResultSet可更新的必要條件

  1. 單表查詢:不涉及JOIN、UNION、子查詢、視圖(部分視圖可更新)。
  2. 包含主鍵或唯一非空列:驅動需能唯一標識行以生成UPDATE/DELETE語句。
  3. 無聚合函數/表達式/常量列:`SELECT id, name, price*1.1` 等不可更新。
  4. 驅動支持:MySQL Connector/J 5.1+ 支持,但需符合上述條件;部分驅動僅支持`TYPE_FORWARD_ONLY` + `CONCUR_UPDATABLE`。

常見導致Read Only的SQL模式

SQL示例原因
`SELECT * FROM t1 JOIN t2 ON ...`多表關聯
`SELECT id, COUNT(*) FROM t GROUP BY id`聚合查詢
`SELECT id, 'constant' AS flag FROM t`常量列
`SELECT id, UPPER(name) AS uname FROM t`表達式列
`SELECT * FROM view_name`視圖可能不可更新
`SELECT id, name FROM t WHERE id IN (SELECT ...)`子查詢(部分驅動不支持)

排查步驟

  1. 檢查SQL:確保符合上述條件,簡化至`SELECT pk, col1, col2 FROM single_table`測試。
  2. 檢查表結構:確認有主鍵或唯一非空索引。
  3. 嘗試不同游標類型:
    
    // 嘗試敏感游標
    Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
    // 或 僅前向只讀但可更新(部分驅動支持)
    Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
    
  4. 驅動版本/配置**:升級MySQL Connector/J至最新;檢查連接URL參數(如`useCursorFetch=true`可能影響)。
  5. 檢查權限**:數據庫用戶需有該表的`UPDATE`權限。

替代方案(推薦)

**不要在業務代碼中使用可更新ResultSet**,理由:

  • 鎖定行時間長,併發性極差。
  • 邏輯分散,難以事務控制、驗證、審計。
  • 移植性差,驅動行為不一致。

標準做法:查詢獲取數據 → 內存中修改對象 → 執行參數化`UPDATE`語句:


String sql = "UPDATE my_table SET column = ? WHERE pk = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, newValue);
    ps.setInt(2, id);
    ps.executeUpdate();
}

若必須用可更新ResultSet(極少數場景)

  • 確保滿足所有必要條件。
  • 調用`updateXxx`後**必須**調用`updateRow()`提交行變更。
  • 捕獲`SQLException`檢查`SQLState`為`01001`(游標只讀警告)。
現代數據訪問原則:查詢用ResultSet/ORM,更新用PreparedStatement/JPA/Hibernate/MyBatis,職責分離才專業。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved