jdbc12.jar (或 ojdbc14.jar 等較新版本)blobtest(name VARCHAR2(20), content BLOB)package dbdriver;
import java.sql.*;
import java.io.*;
import oracle.sql.*;
public class WriteBlob {
public static void main(String[] args) {
try {
// 1. 準備檔案路徑
String fileName = "E:\\jianxin.bmp";
File f = new File(fileName);
String fName = f.getName();
int i = fName.lastIndexOf('.');
String fName2 = (i > 0 && i < fName.length() - 1) ? fName.substring(0, i) : fName;
System.out.println("檔案名(無副檔名): " + fName2);
// 2. 註冊驅動並建立連線
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:XE", "system", "duduli");
conn.setAutoCommit(false); // 手動控制交易
// 3. 先插入空 BLOB 佔位符,取得 BLOB 定位器
BLOB blob = null;
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO blobtest(name, content) VALUES(?, empty_blob())");
pstmt.setString(1, fName2);
pstmt.executeUpdate();
pstmt.close();
// 4. 為 UPDATE 鎖定該列,取得可寫入的 BLOB 物件
pstmt = conn.prepareStatement(
"SELECT content FROM blobtest WHERE name = ? FOR UPDATE");
pstmt.setString(1, fName2);
ResultSet rset = pstmt.executeQuery();
if (rset.next()) {
blob = (BLOB) rset.getBlob(1);
}
pstmt.close();
// 5. 讀取檔案並寫入 BLOB
FileInputStream fin = new FileInputStream(f);
System.out.println("檔案大小: " + fin.available() + " bytes");
OutputStream out = blob.getBinaryOutputStream();
byte[] data = new byte[fin.available()];
fin.read(data);
out.write(data);
fin.close();
out.close();
// 6. 更新資料庫確認寫入
pstmt = conn.prepareStatement(
"UPDATE blobtest SET content = ? WHERE name = ?");
pstmt.setBlob(1, blob);
pstmt.setString(2, fName2);
pstmt.executeUpdate();
pstmt.close();
// 7. 提交交易並關閉連線
conn.commit();
conn.close();
System.out.println("圖片寫入 BLOB 成功!");
} catch (SQLException e) {
System.err.println("SQL 錯誤: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("IO 錯誤: " + e.getMessage());
}
}
}
Oracle BLOB 寫入需分兩步:
oracle.sql.BLOB 物件 (含定位器)直接 INSERT INTO ... VALUES(?, ?) 設定 setBlob 在某些 Oracle 版本/驅動下會失敗,因為 JDBC 驅動無法直接串流寫入。
OutputStream out = blob.getBinaryOutputStream(); // 取得 BLOB 輸出串流
byte[] data = new byte[fin.available()]; // 讀取完整檔案到記憶體
fin.read(data);
out.write(data); // 寫入資料庫
out.close(); // 關閉串流,完成寫入
注意: 此範例一次性讀取整個檔案到記憶體,大檔案建議分塊讀寫 (如 4KB/8KB 緩衝區循環)。
conn.setAutoCommit(false):關閉自動提交,確保寫入原子性conn.commit():確認寫入原因: Oracle JDBC 不允許串流操作批次處理。
解決: 先寫入空 BLOB/Clob,flush() 並 refresh() 鎖定列,再寫入實際內容:
user.setImage(Hibernate.createBlob(new byte[1]));
user.setDesc(Hibernate.createClob(" "));
session.save(user);
session.flush();
session.refresh(user, LockMode.UPGRADE);
// 此後可正常寫入 blob/clob 實際內容
解決: 分塊讀寫,避免一次性載入完整檔案:
FileInputStream fin = new FileInputStream(f);
OutputStream out = blob.getBinaryOutputStream();
byte[] buffer = new byte[8192]; // 8KB 緩衝區
int bytesRead;
while ((bytesRead = fin.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
fin.close();
out.close();
確保資料庫使用者擁有 CREATE TABLE、INSERT、UPDATE、SELECT ... FOR UPDATE 權限。
// 1. 取得 BLOB 物件
Blob img = user.getImage(); // 或從 ResultSet 取得
InputStream in = img.getBinaryStream();
// 2. 寫入檔案
FileOutputStream fos = new FileOutputStream("tempimg.jpg");
byte[] buffer = new byte[102400];
int len;
while ((len = in.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
in.close();
oracle.sql.Clob clob = user.getDesc();
Writer writer = clob.getCharacterOutputStream();
writer.write("大量文字內容...");
writer.close();
session.save(user);
PreparedStatement 防止 SQL 注入oracle.sql.BLOB/CLOB) 功能最完整,但降低可攜性;標準 JDBC java.sql.Blob/Clob 可攜性較好