你打电话时session.save()
,hibernate会生成一条INSERT SQL。此 INSERT SQL 将在刷新期间附加到数据库中(即session.flush()
) .
冲洗过程中,如果hibernate.jdbc.batch_size
设置为某个非零值时,Hibernate 将使用 JDBC2 API 中引入的批处理功能向 DB 发出批量插入 SQL。
例如,如果您save()
100 条记录和您的hibernate.jdbc.batch_size
设置为 50。在刷新期间,不要发出以下 SQL 100 次:
insert into TableA (id , fields) values (1, 'val1');
insert into TableA (id , fields) values (2, 'val2');
insert into TableA (id , fields) values (3, 'val3');
.........................
insert into TableA (id , fields) values (100, 'val100');
Hibernate 会将它们分成 50 个批次,并且只向数据库发出 2 个 SQL,如下所示:
insert into TableA (id , fields) values (1, 'val1') , (2, 'val2') ,(3, 'val3') ,(4, 'val4') ,......,(50, 'val50')
insert into TableA (id , fields) values (51, 'val51') , (52, 'val52') ,(53, 'val53') ,(54, 'val54'),...... ,(100, 'val100')
请注意,如果插入表的主键是,Hibernate 将透明地在 JDBC 级别禁用插入批处理GenerationType.Identity
.
从你的日志中:你save()
只有一条记录,然后flush()
,因此每次刷新时只需要处理一个附加 INSERT SQL。这就是为什么Hibernate无法帮助你批量插入,因为只有一条INSERT SQL需要处理。你应该save()
呼叫前达到一定数量的记录flush()
而不是打电话flush()
对于每一个save()
.
批量插入的最佳实践是这样的:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<888888; i++ ) {
TableA record = new TableA();
record.setXXXX();
session.save(record)
if ( i % 50 == 0 ) { //50, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
您可以批量保存并刷新记录。在每个批次结束时,您应该清除持久性上下文以释放一些内存,以防止内存耗尽,因为每个持久性对象都被放入第一级缓存(JVM 的内存)中。您还可以禁用二级缓存以减少不必要的开销。
参考:
- Hibernate 官方文档:第 14 章. 批处理
- Hibernate 批处理 – 为什么您可能不使用它。 (即使你认为你是)