java.sql.SQLException: Internal Error: Unable to construct a Datum from the specified input
發生環境:Oracle 8.1.7,JDBC 更新含 BLOB 欄位的資料表,使用 ResultSet.updateBinaryStream() 時拋出。
使用了 FileInputStream 作為輸入流來源。FileInputStream 未完整實作 java.io.InputStream 介面的所有方法,Oracle JDBC 驅動程式在建構 Datum 物件時需要呼叫這些方法,導致失敗。
改用 java.io.File 物件直接傳遞,或包裝為支援完整介面的流物件。
// 錯誤寫法
FileInputStream fis = new FileInputStream("image.jpg");
resultSet.updateBinaryStream("blob_col", fis, file.length());
// 寫法 1:直接使用 File 物件 (Oracle JDBC 擴充支援)
File file = new File("image.jpg");
resultSet.updateBinaryStream("blob_col", file);
// 寫法 2:使用 FileInputStream 但包裝為 BufferedInputStream
FileInputStream fis = new FileInputStream("image.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
resultSet.updateBinaryStream("blob_col", bis, file.length());
// 寫法 3:使用 PreparedStatement + setBinaryStream (推薦)
PreparedStatement ps = conn.prepareStatement(
"UPDATE table SET blob_col = ? WHERE id = ?");
File file = new File("image.jpg");
ps.setBinaryStream(1, new FileInputStream(file), file.length());
ps.setInt(2, id);
ps.executeUpdate();
DBMS_LOB 或 oracle.sql.BLOB API 分段寫入public void updateBlob(int id, File file) throws SQLException, IOException {
String sql = "UPDATE my_table SET blob_data = ? WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
FileInputStream fis = new FileInputStream(file)) {
ps.setBinaryStream(1, fis, file.length());
ps.setInt(2, id);
ps.executeUpdate();
}
}
// 讀取並寫入檔案
ResultSet rs = stmt.executeQuery("SELECT blob_data FROM my_table WHERE id = 1");
if (rs.next()) {
Blob blob = rs.getBlob("blob_data");
try (InputStream is = blob.getBinaryStream();
FileOutputStream fos = new FileOutputStream("output.jpg")) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
}