在處理大量數據導入、日誌收集、報表生成等場景時,逐條執行 session.save() 會導致頻繁的數據庫交互、內存壓力激增甚至 OOM。Hibernate 批量操作通過 flush() 和 clear() 機制,可將內存中的持久化對象定期同步到數據庫並釋放 Session 緩存,大幅提升性能。
hibernate.cfg.xml 中配置 hibernate.jdbc.batch_size (建議 20-50)session.flush() 同步 SQL,session.clear() 清理一級緩存StatelessSession 無一級緩存,適合純批量導入場景package com.xqh.model;
public class Medicine {
private Integer id;
private String name;
private double price;
private String factoryAdd;
// Getter/Setter 省略...
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"https://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.xqh.model.Medicine" table="tb_medicine_batch">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="200"/>
<property name="price" not-null="true"/>
<property name="factoryAdd" length="200"/>
</class>
</hibernate-mapping>
package com.xqh.dao;
import java.util.List;
import org.hibernate.Session;
import com.xqh.model.Medicine;
import com.xqh.util.HibernateUtil;
public class MedicineDao {
public void saveMedicines(List<Medicine> medicines) {
Session session = null;
if (medicines == null || medicines.isEmpty()) return;
try {
session = HibernateUtil.getSession();
session.beginTransaction();
for (int i = 0; i < medicines.size(); i++) {
session.save(medicines.get(i));
// 關鍵優化:每 10 條刷新並清理緩存
if (i % 10 == 0) {
session.flush();
session.clear();
}
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if (session != null) session.getTransaction().rollback();
} finally {
HibernateUtil.closeSession(session);
}
}
}
package com.xqh.util;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory factory;
static {
try {
Configuration cfg = new Configuration().configure();
factory = cfg.buildSessionFactory();
} catch (HibernateException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Session getSession() {
return factory != null ? factory.openSession() : null;
}
public static void closeSession(Session session) {
if (session != null && session.isOpen()) session.close();
}
public static SessionFactory getSessionFactory() { return factory; }
}
<property name="hibernate.jdbc.batch_size">20</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.order_updates">true</property>
<property name="hibernate.jdbc.batch_versioned_data">true</property>
package com.xqh.servlet;
import java.io.IOException;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.xqh.dao.MedicineDao;
import com.xqh.model.Medicine;
public class SaveMedicine extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String[] names = req.getParameterValues("name");
String[] prices = req.getParameterValues("price");
String[] adds = req.getParameterValues("factoryAdd");
if (names != null && prices != null && adds != null
&& names.length == prices.length && names.length == adds.length) {
List<Medicine> list = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
Medicine m = new Medicine();
m.setName(names[i]);
m.setPrice(Double.parseDouble(prices[i]));
m.setFactoryAdd(adds[i]);
list.add(m);
}
new MedicineDao().saveMedicines(list);
req.setAttribute("info", "藥品信息批量保存成功!");
}
req.getRequestDispatcher("result.jsp").forward(req, resp);
}
}
<%@ page contentType="text/html;charset=GBK" %>
<html><head><title>批量添加藥品信息</title>
<script>
function addRow() {
document.getElementById('container').innerHTML +=
document.getElementById('template').innerHTML;
}
function removeRow() {
var container = document.getElementById('container');
var html = container.innerHTML;
var lastRow = html.lastIndexOf('<table');
if (lastRow > 0) container.innerHTML = html.substring(0, lastRow);
}
function validate(form) {
for (var i = 0; i < form.elements.length; i++) {
if (!form.elements[i].value) {
alert("請填寫完整信息!");
return false;
}
}
return true;
}
</script></head>
<body onload="addRow()">
<form action="SaveMedicine" method="post" onsubmit="return validate(this)">
<table align="center">
<tr><td colspan="6" align="center"><h1>批量添加藥品信息</h1></td></tr>
<tr><td><div id="container"></div></td></tr>
<tr><td>
<input type="button" value="添加一行" onclick="addRow()">
<input type="button" value="減少一行" onclick="removeRow()">
<input type="submit" value="批量提交到數據庫">
</td></tr>
</table>
</form>
<div id="template" style="display:none">
<table><tr>
<td>名稱:</td><td><input name="name" size="13"></td>
<td>價格:</td><td><input name="price" size="13"></td>
<td>廠址:</td><td><input name="factoryAdd" size="30"></td>
</tr></table></div>
</body></html>
| 方案 | 1萬條耗時 | 內存佔用 | 適用場景 |
|---|---|---|---|
| 普通 save() | ~45秒 | 高 (OOM風險) | 少量數據 |
| flush/clear 優化 | ~8秒 | 低 | 通用批量導入 |
| StatelessSession | ~5秒 | 極低 | 純導入、無關聯對象 |
| 原生 SQL + PreparedStatement | ~3秒 | 最低 | 極致性能要求 |
identity 或 sequence 時,Hibernate 無法批處理 INSERT;改用 enhanced-sequence 或分配器模式通過合理配置批處理參數、定期清理 Session 緩存,Hibernate 完全能滿足百萬級數據導入的性能需求。實際項目中建議結合業務特點,在開發階段即進行壓力測試驗證效果。