java - pedido - modelos de inventarios deterministicos pdf
Hibernate confusión de tamaño de lote (3)
Este programa hace decenas de miles de insertos consecutivos uno tras otro. Nunca he usado Hibernate antes. Estoy obteniendo un rendimiento extremadamente lento (si solo conecto y ejecuto el SQL manualmente, soy 10-12x más rápido. Mi batch_size está configurado en 50 según muchos tutoriales de hibernación.
Aquí hay un registro de un solo inserto, tal vez podrías ayudarme a entender exactamente lo que está sucediendo:
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
Cuando llame a session.save()
, hibernate generará un INSERT SQL. Este INSERTAR SQL se adjuntará para emitirse a la base de datos durante la descarga (es decir, session.flush()
).
Durante la descarga, si hibernate.jdbc.batch_size
se establece en algún valor distinto de cero, Hibernate utilizará la función de procesamiento por lotes introducida en la API JDBC2 para emitir el SQL de inserción por lotes al DB.
Por ejemplo, si save()
100 registros y su hibernate.jdbc.batch_size
se establece en 50. Durante la descarga, en lugar de emitir el siguiente SQL 100 veces:
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'');
Hiberate los agrupará en lotes de 50, y solo emitirá 2 SQL al DB, así:
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'')
Tenga en cuenta que Hibernate inhabilitará la inserción de lotes en el nivel JDBC de forma transparente si la clave principal de la tabla de inserción es GenerationType.Identity
.
Desde su registro: save()
solo un registro y luego flush()
, por lo que solo se agrega un INSERT SQL para cada descarga. Es por eso que Hibernate no puede ayudarte a insertar por lotes, ya que solo hay un INSERT SQL para procesar. Debe save()
hasta la cantidad determinada de registros antes de llamar a flush()
lugar de llamar a flush()
para cada save()
.
La mejor práctica de inserción de lotes es algo como esto:
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();
Guarda y purga los registros lote por lote. Al final de cada lote, debe borrar el contexto de persistencia para liberar algo de memoria para evitar el agotamiento de la memoria ya que cada objeto persistente se coloca en el primer nivel de caché (la memoria de su JVM). También podría desactivar la memoria caché de segundo nivel para reducir la sobrecarga innecesaria.
Referencia:
Si debe usar hibernate para grandes trabajos por lotes, StatelessSession es el camino a seguir. Reduce las cosas al mapeo de conversión de objetos-a-SQL más básico y elimina toda la sobrecarga de las características de ORM que no estás usando cuando solo atiborras filas al por mayor de DB.
También sería mucho más fácil hacer sugerencias sobre su código real que el registro :)
11:02:56.133 [main] DEBUG o.h.transaction.JDBCTransaction - commit
Esto significa que la base de datos se está comprometiendo después de cada inserción. Asegúrese de no comprometer su transacción / cerrar su sesión dentro del ciclo de inserción. Haga esto una vez al final en su lugar.