項目早期常用 Hibernate/JPA 等全功能 ORM,但面對大數據量(百萬級+)批量寫入、複雜報表查詢時,ORM 存在:
輕量級 DbHelper 直接封裝 JDBC,提供:參數化查詢、批量預編譯、流式結果集、事務模板,適合大數據量場景。
public class DbHelper {
private String sql;
private List<Object> params; // 單條參數
private List<List<Object>> batchParams; // 批量參數
private Connection conn;
public DbHelper() {
this.conn = getConnection(); // 從配置文件讀取屬性構建連接
}
// 配置文件 db.properties
// driver_class=com.mysql.cj.jdbc.Driver
// driver_url=jdbc:mysql://host:3306/db?useSSL=false&serverTimezone=UTC
// database_user=user
// database_password=pwd
private Connection getConnection() { ... }
// 資源關閉
private void closeAll(Connection c, Statement s, ResultSet rs) { ... }
// 單條查詢
public Result executeQuery() { ... }
// 單條增刪改
public int executeUpdate() { ... }
// 批量增刪改(PreparedStatement.addBatch)
public int[] executeBatch() { ... }
// 批量執行任意 SQL 字符串(Statement.addBatch)
public int[] executeBatchSql(List<String> sqls) { ... }
}
private void bindParams(PreparedStatement ps, List<Object> params) throws SQLException {
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
}
public int[] executeBatch() {
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (List<Object> paramSet : batchParams) {
bindParams(ps, paramSet);
ps.addBatch();
}
return ps.executeBatch(); // 返回各條影響行數
} catch (SQLException e) {
throw new RuntimeException("批量執行失敗", e);
}
}
public void executeLargeQuery(Consumer<ResultSet> handler) {
try (Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
stmt.setFetchSize(Integer.MIN_VALUE); // MySQL 流式讀取關鍵設置
try (ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
handler.accept(rs); // 逐行處理,不佔用堆內存
}
}
} catch (SQLException e) {
throw new RuntimeException("大查詢失敗", e);
}
}
public T executeInTransaction(Function action) {
boolean originalAutoCommit = conn.getAutoCommit();
try {
conn.setAutoCommit(false);
T result = action.apply(conn);
conn.commit();
return result;
} catch (Exception e) {
conn.rollback();
throw new RuntimeException("事務失敗", e);
} finally {
conn.setAutoCommit(originalAutoCommit);
}
}
DbHelper helper = new DbHelper();
helper.setSql("INSERT INTO tb_log(type, content, create_time) VALUES (?, ?, NOW())");
helper.setParams(Arrays.asList("ERROR", "空指針異常"));
helper.executeUpdate();
DbHelper helper = new DbHelper();
helper.setSql("INSERT INTO tb_user(name, email, status) VALUES (?, ?, ?)");
List> batch = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
batch.add(Arrays.asList("user" + i, "user" + i + "@example.com", 1));
if (batch.size() == 1000) { // 分批提交,避免參數集過大
helper.setBatchParams(new ArrayList<>(batch));
helper.executeBatch();
batch.clear();
}
}
if (!batch.isEmpty()) {
helper.setBatchParams(batch);
helper.executeBatch();
}
DbHelper helper = new DbHelper();
helper.setSql("SELECT id, name, email FROM tb_user WHERE status = 1");
helper.executeLargeQuery(rs -> {
// 寫入 CSV、推送到消息隊列、增量同步 ES 等
csvWriter.write(rs.getLong(1), rs.getString(2), rs.getString(3));
});
helper.executeInTransaction(conn -> {
try (PreparedStatement ps1 = conn.prepareStatement("UPDATE account SET balance = balance - ? WHERE id = ?");
PreparedStatement ps2 = conn.prepareStatement("UPDATE account SET balance = balance + ? WHERE id = ?")) {
ps1.setBigDecimal(1, amount); ps1.setLong(2, fromId);
ps2.setBigDecimal(1, amount); ps2.setLong(2, toId);
ps1.executeUpdate();
ps2.executeUpdate();
return true;
}
});
executeBatch 過大導致網絡阻塞或數據庫鎖升級。conn.setAutoCommit(false),提交後恢復。setFetchSize(Integer.MIN_VALUE)、PostgreSQL setFetchSize(1000) + 游標,避免全量載入內存。PreparedStatement(需連接池支持 Statement Cache)。DbHelper 適合:批量導入/導出、報表查詢、異構同步、存儲過程調用等「SQL 為主導」場景。業務邏輯複雜、對象圖豐富的核心域模型,仍建議使用 JPA/MyBatis。混合架構:核心域用 ORM,大數據邊緣用 DbHelper。