首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JDBC 操作 MySQL 大字段完全指南:BLOB 與 CLOB 串流讀寫實戰

     編輯:Java 相關知識

MySQL 大字段類型總覽

類型最大長度特性
TINYBLOB / TINYTEXT255 位元組/字元極小二進位/文本
BLOB / TEXT65,535 (64KB)標準大小
MEDIUMBLOB / MEDIUMTEXT16,777,215 (16MB)中等大物件
LONGBLOB / LONGTEXT4,294,967,295 (4GB)超大物件,受限於 max_allowed_packet 與記憶體

關鍵差異:BLOB 視為二進位字串(無字符集、按位元組比較);TEXT 視為字符字串(有字符集、按排序規則比較)。

測試環境建立

CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  `pswd` VARCHAR(50) DEFAULT NULL,
  `pic` LONGBLOB,
  `remark` LONGTEXT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

BLOB 二進位串流讀寫

寫入(PreparedStatement + BinaryStream)

public static void insertBlob() {
    Connection conn = DBToolkit.getConnection();
    String sql = "INSERT INTO user (name, pswd, pic) VALUES (?, ?, ?)";
    try (PreparedStatement ps = conn.prepareStatement(sql);
         InputStream in = new BufferedInputStream(new FileInputStream("D:/PIC.PNG"))) {
        
        ps.setString(1, "zhangsan");
        ps.setString(2, "111");
        ps.setBinaryStream(3, in, (int) new File("D:/PIC.PNG").length());
        ps.executeUpdate();
    } catch (IOException | SQLException e) {
        e.printStackTrace();
    }
}

讀取(ResultSet.getBinaryStream)

public static void queryBlob() {
    String sql = "SELECT pic FROM user WHERE id = 24";
    try (Connection conn = DBToolkit.getConnection();
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        
        if (rs.next()) {
            try (InputStream in = rs.getBinaryStream(1);
                 OutputStream out = new BufferedOutputStream(new FileOutputStream("D:/PIC_COPY.PNG"))) {
                
                byte[] buf = new byte[1024];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            }
        }
    } catch (IOException | SQLException e) {
        e.printStackTrace();
    }
}
注意:setBinaryStream 第三參數長度必須準確,否則可能寫入不完整。大檔案建議分塊寫入或使用 setBlob。

CLOB 字符串流讀寫與編碼陷阱

寫入(CharacterStream)

public static void insertClob() {
    String sql = "INSERT INTO user (name, pswd, remark) VALUES (?, ?, ?)";
    try (Connection conn = DBToolkit.getConnection();
         PreparedStatement ps = conn.prepareStatement(sql);
         Reader reader = new InputStreamReader(new FileInputStream("D:/TEXT.txt"), "UTF-8")) {
        
        ps.setString(1, "zhangsan");
        ps.setString(2, "111");
        ps.setCharacterStream(3, reader, (int) new File("D:/TEXT.txt").length());
        ps.executeUpdate();
    } catch (IOException | SQLException e) {
        e.printStackTrace();
    }
}

讀取(getCharacterStream)

public static void queryClob() {
    String sql = "SELECT remark FROM user WHERE id = 1";
    try (Connection conn = DBToolkit.getConnection();
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        
        if (rs.next()) {
            try (Reader reader = rs.getCharacterStream(1);
                 Writer writer = new OutputStreamWriter(new FileOutputStream("D:/TEXT_COPY.txt"), "UTF-8")) {
                
                char[] buf = new char[1024];
                int len;
                while ((len = reader.read(buf)) > 0) {
                    writer.write(buf, 0, len);
                }
            }
        }
    } catch (IOException | SQLException e) {
        e.printStackTrace();
    }
}

編碼問題深度解析

現象:直接使用 getCharacterStream() 讀取的 CLOB,中文可能亂碼。

原因:JDBC 驅動在內部已按連接字符集(characterEncoding 參數)將二進位數據解碼為 char 陣列。若資料庫存儲編碼、連接編碼、JVM 默認編碼三者不一致,會發生雙重編碼/解碼錯誤。

解決方案:

  1. 統一編碼鏈路:資料庫/表/欄位 CHARSET=utf8mb4,JDBC URL 附加 ?characterEncoding=UTF-8&useUnicode=true,JVM -Dfile.encoding=UTF-8。
  2. 繞過 CLOB,改用 BLOB 存儲文本:自行控制編碼/解碼,彻底消除驅動層不確定性。
  3. 使用 setClob/getClob + 顯式編碼(JDBC 4.0+):
// 寫入
Clob clob = conn.createClob();
clob.setString(1, new String(Files.readAllBytes(path), "UTF-8"));
ps.setClob(3, clob);

// 讀取
Clob clob = rs.getClob(1);
String content = clob.getSubString(1, (int) clob.length());

最佳實踐清單

  • 連接池配置 max_allowed_packet 足夠大(如 64M)。
  • 大物件讀寫務必使用緩衝流(BufferedInputStream/OutputStream)。
  • 生產環境避免 SELECT * 拉取大字段,按需查詢。
  • 超大檔案(>100MB)考慮外部存儲(對象存儲/檔案系統),資料庫僅存 URI。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved