conectar - hibernate insertar lote con postgresql particionado
hibernate sequence postgresql (5)
¿Hay alguna solución para la inserción de lotes a través de la hibernación en la tabla postgresql particionada? Actualmente recibo un error como este ...
ERROR org.hibernate.jdbc.AbstractBatcher - Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)....
he encontrado este enlace http://lists.jboss.org/pipermail/hibernate-dev/2007-October/002771.html pero no puedo encontrar ningún sitio en la web donde se resuelva este problema o cómo se puede solucionar
Aparece si puede usar REGLAS en lugar de desencadenantes para la inserción, entonces puede devolver el número correcto, pero solo con una sola REGLA sin una instrucción WHERE.
Otra opción puede ser crear una vista que "envuelva" la tabla particionada, luego devuelva la fila NUEVA para indicar una actualización correcta de la fila, sin agregar accidentalmente una fila extra no deseada a la tabla maestra.
create view tablename_view as select * from tablename; -- create trivial wrapping view
CREATE OR REPLACE FUNCTION partitioned_insert_trigger() -- partitioned insert trigger
RETURNS TRIGGER AS $$
BEGIN
IF (NEW.partition_key>= 5500000000 AND
NEW.partition_key < 6000000000) THEN
INSERT INTO tablename_55_59 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 5000000000 AND
NEW.partition_key < 5500000000) THEN
INSERT INTO tablename_50_54 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 500000000 AND
NEW.partition_key < 1000000000) THEN
INSERT INTO tablename_5_9 VALUES (NEW.*);
ELSIF (NEW.partition_key >= 0 AND
NEW.partition_key < 500000000) THEN
INSERT INTO tablename_0_4 VALUES (NEW.*);
ELSE
RAISE EXCEPTION ''partition key is out of range. Fix the trigger function'';
END IF;
RETURN NEW; -- RETURN NEW in this case, typically you''d return NULL from this trigger, but for views we return NEW
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER insert_view_trigger
INSTEAD OF INSERT ON tablename_view
FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger(); -- create "INSTEAD OF" trigger
ref: http://www.postgresql.org/docs/9.2/static/trigger-definition.html
Si fue a la ruta de envoltura de vista una opción es definir también desencadenantes triviales "en lugar de" para eliminar y actualizar, también puede usar el nombre de la tabla de vista en lugar de su tabla normal en todas las transacciones.
Otra opción que utiliza la vista es crear una regla de inserción para que cualquier inserción en la tabla principal vaya a la vista [que usa su desencadenador], ex (suponiendo que ya tiene partitioned_insert_trigger
y tablename_view e insert_view_trigger creados como se indica más arriba)
create RULE use_right_inserter_tablename AS
ON INSERT TO tablename
DO INSTEAD insert into tablename_view VALUES (NEW.*);
Luego usará su nuevo inserto de envoltura de vista de trabajo.
Dicen que utilicen dos desencadenantes en una tabla particionada o la anotación @SQLInsert aquí: http://www.redhat.com/f/pdf/jbw/jmlodgenski_940_scaling_hibernate.pdf páginas 21-26 (también menciona un @SQLInsert especificando una Cadena método).
Aquí hay un ejemplo con un desencadenador posterior para eliminar la fila adicional en el maestro: https://gist.github.com/copiousfreetime/59067
Me enfrenté al mismo problema mientras insertaba documentos a través de Hibernate después de que la búsqueda descubrió que esperaba que se devolvieran las filas actualizadas, así que en lugar de nulo, cámbielo a nuevo en el procedimiento desencadenante, que resolverá el problema como se muestra a continuación.
REGRESAR NUEVO
Puede intentar usar un Batcher personalizado configurando la propiedad hibernate.jdbc.factory_class. Asegurándose de que hibernar no verificará la cuenta de actualización de las operaciones por lotes podría solucionar su problema, puede lograrlo haciendo que su Batcher personalizado amplíe la clase BatchingBatcher, y luego anule el método doExecuteBatch (...) para que se vea así:
@Override
protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
if ( batchSize == 0 ) {
log.debug( "no batched statements to execute" );
}
else {
if ( log.isDebugEnabled() ) {
log.debug( "Executing batch size: " + batchSize );
}
try {
// checkRowCounts( ps.executeBatch(), ps );
ps.executeBatch();
}
catch (RuntimeException re) {
log.error( "Exception executing batch: ", re );
throw re;
}
finally {
batchSize = 0;
}
}
}
Tenga en cuenta que el nuevo método no verifica los resultados de la ejecución de las declaraciones preparadas. Tenga en cuenta que realizar este cambio podría afectar la hibernación de alguna forma inesperada (o tal vez no).
THX! funcionó, sin problemas, hasta ahora:) .... una cosa ... Tuve que implementar la clase BatcherFactory
y ponerla en el archivo persistence.xml
, así:
property name="hibernate.jdbc.factory_class" value="path.to.my.batcher.factory.implementation"
de esa fábrica llamé a mi implementación de dosificador con el código anterior
ps hibernate core 3.2.6 GA
Gracias una vez más