Java 休眠批量大小混乱
这个程序一个接一个地执行数万次连续插入。我以前从未使用过Hibernate。我的性能非常慢(如果我只是手动连接并执行SQL,我会快10-12倍。根据许多hibernate教程,我的批处理大小设置为50) 这是一个单次插入的日志-也许您可以帮助我准确了解发生了什么:Java 休眠批量大小混乱,java,hibernate,jdbc,Java,Hibernate,Jdbc,这个程序一个接一个地执行数万次连续插入。我以前从未使用过Hibernate。我的性能非常慢(如果我只是手动连接并执行SQL,我会快10-12倍。根据许多hibernate教程,我的批处理大小设置为50) 这是一个单次插入的日志-也许您可以帮助我准确了解发生了什么: START INSERT 11:02:56.121 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13106053761
START INSERT
11:02:56.121 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13106053761
11:02:56.121 [main] DEBUG o.h.transaction.JDBCTransaction - begin
11:02:56.121 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
11:02:56.121 [main] TRACE o.h.c.DriverManagerConnectionProvider - total checked-out connections: 0
11:02:56.121 [main] TRACE o.h.c.DriverManagerConnectionProvider - using pooled JDBC connection, pool size: 0
11:02:56.121 [main] DEBUG o.h.transaction.JDBCTransaction - current autocommit status: false
11:02:56.121 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction begin
11:02:56.121 [main] TRACE org.hibernate.impl.SessionImpl - setting flush mode to: MANUAL
11:02:56.121 [main] TRACE o.h.e.def.DefaultLoadEventListener - loading entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}]
11:02:56.121 [main] TRACE o.h.e.def.DefaultLoadEventListener - creating new proxy for entity
11:02:56.122 [main] TRACE o.h.e.d.DefaultSaveOrUpdateEventListener - saving transient instance
11:02:56.122 [main] DEBUG o.h.e.def.AbstractSaveEventListener - generated identifier: component[keyW000]{keyW000=F000 ADSUFC}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
11:02:56.122 [main] TRACE o.h.e.def.AbstractSaveEventListener - saving [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}]
11:02:56.123 [main] TRACE o.h.e.d.AbstractFlushingEventListener - flushing session
11:02:56.123 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades
11:02:56.123 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections
11:02:56.123 [main] TRACE o.h.e.d.AbstractFlushingEventListener - Flushing entities and processing referenced collections
11:02:56.125 [main] TRACE o.h.e.d.AbstractFlushingEventListener - Processing unreferenced collections
11:02:56.125 [main] TRACE o.h.e.d.AbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates
11:02:56.126 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 62 objects
11:02:56.126 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
11:02:56.132 [main] TRACE o.h.e.d.AbstractFlushingEventListener - executing flush
11:02:56.132 [main] TRACE org.hibernate.jdbc.ConnectionManager - registering flush begin
11:02:56.132 [main] TRACE o.h.p.entity.AbstractEntityPersister - Inserting entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}]
11:02:56.132 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
11:02:56.132 [main] DEBUG org.hibernate.SQL - insert into MSW000 (W000_DATA_REC, W000_FILE_FLAGS, KEY_W000) values (?, ?, ?)
11:02:56.132 [main] TRACE org.hibernate.jdbc.AbstractBatcher - preparing statement
11:02:56.132 [main] TRACE o.h.p.entity.AbstractEntityPersister - Dehydrating entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}]
11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding ' ADSUFCA ' to parameter: 1
11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding ' ' to parameter: 2
11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding 'F000 ADSUFC' to parameter: 3
11:02:56.132 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
11:02:56.133 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
11:02:56.133 [main] TRACE org.hibernate.jdbc.AbstractBatcher - closing statement
11:02:56.133 [main] TRACE org.hibernate.jdbc.ConnectionManager - registering flush end
11:02:56.133 [main] TRACE o.h.e.d.AbstractFlushingEventListener - post flush
11:02:56.133 [main] DEBUG o.h.transaction.JDBCTransaction - commit
11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - automatically flushing session
11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - before transaction completion
11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - before transaction completion
11:02:56.133 [main] DEBUG o.h.transaction.JDBCTransaction - committed JDBC Connection
11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - after transaction completion
11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - closing session
11:02:56.133 [main] TRACE org.hibernate.jdbc.ConnectionManager - performing cleanup
11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
11:02:56.133 [main] TRACE o.h.c.DriverManagerConnectionProvider - returning connection to pool, pool size: 1
11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
11:02:56.134 [main] TRACE org.hibernate.impl.SessionImpl - after transaction completion
FINISH INSERT
这意味着数据库在每次插入后都会提交。请确保您没有在插入循环中提交事务/关闭会话。请在插入循环结束时执行此操作。如果您必须使用hibernate进行大量批处理作业,则可以这样做。它可以将事情简化为将对象转换为SQL语句的最基本步骤应用并消除了ORM特性的所有开销,这些特性在将行填充到DB中时没有使用
与日志相比,对实际代码提出建议也要容易得多:)当您调用
session.save()
时,hibernate将生成一个INSERT SQL。此INSERT SQL将被追加,以便在刷新(即session.flush()
)期间发布到DB
在刷新期间,如果将hibernate.jdbc.batch_size
设置为非零值,hibernate将使用JDBC2 API中引入的批处理功能向数据库发出批插入SQL
例如,如果save()
100条记录,并且hibernate.jdbc.batch\u 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');
Hibrate将它们分为50个批次进行分组,并且只向DB发出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')
请注意,如果插入表的主键是GenerationType.Identity
,Hibernate将在JDBC级别透明地禁用插入批处理
从日志中:您save()
只保存一条记录,然后flush()
,因此每次flush只能处理一条追加插入SQL。这就是为什么Hibernate不能帮助您批量插入,因为只有一个INSERT SQL需要处理。在调用flush()
之前,您应该save()
最多保存一定数量的记录,而不是每次save()
都调用flush()
批量插入的最佳实践如下:
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();
Session Session=sessionFactory.openSession();
事务tx=会话.beginTransaction();
对于(inti=0;我知道会的。代码实际上是naca生成的java(来自COBOL)因此,调试和恢复您的头脑是非常困难的。仍然没有找到插入的位置。但是,您应该每隔大约1000条记录刷新并清除您的会话。@jtahlborn是的,非常好的观点,否则Hibernate将耗尽内存。如果我能给您更多的投票,我会的。我设法解决了它,但谢谢您nyway!我想知道为什么在增加循环索引I
之前刷新并清除会话。我想你应该从for
循环中删除I++
,如果条件为++I%50==0(Hibernate文档中的错误?)@dylanyi这只是一个例子。可能索引i与扫描源数组有关(请参见record.setXXX:它从哪里获取数据?)当您处理数以百万计的记录时,开始时0处的刷新并没有什么区别。默认情况下,Hibernates只生成一条INSERT语句,然后使用JDBC批处理功能为每个参数绑定值数组,而不是每个参数绑定一个值。如果提供程序(例如PostgreSQL、MySQL、FireBird或SQLite3)不支持数组绑定,它是通过将行作为单个插入运行来模拟的。请注意,有些操作可以更改此默认行为,并启用多插入。@Transactional
将帮助您提交(触发刷新)当该方法成功完成时。因此,如果您仅依赖于@Transactional
,则在TX提交时,它将只刷新一次。我们要做的是手动逐批刷新会话。
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();