Hibernate upsert idempotente flujos seguros y sin manejo de excepciones restricción?

Alex R:

Tengo algo de código que realiza una UPSERT, también conocidos como Merge . Quiero limpieza de este código, en concreto, quiero alejarse de manejo de excepciones, y reducir la verbosidad general y la enorme complejidad del código para una operación tan sencilla. El requisito es que insertar cada artículo a menos que ya existe:

public void batchInsert(IncomingItem[] items) {
    try(Session session = sessionFactory.openSession()) {
        batchInsert(session, items);
    }
    catch(PersistenceException e) {
        if(e.getCause() instanceof ConstraintViolationException) {
            logger.warn("attempting to recover from constraint violation");
            DateTimeFormatter dbFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
            items = Arrays.stream(items).filter(item -> {
                int n = db.queryForObject("select count(*) from rets where source = ? and systemid = ? and updtdate = ?::timestamp",
                        Integer.class,
                        item.getSource().name(), item.getSystemID(), 
                        dbFormat.format(item.getUpdtDateObj()));
                if(n != 0) {
                    logger.warn("REMOVED DUPLICATE: " +
                            item.getSource() + " " + item.getSystemID() + " " + item.getUpdtDate());
                    return false;
                }
                else {
                    return true; // keep
                }
            }).toArray(IncomingItem[]::new);
            try(Session session = sessionFactory.openSession()) {
                batchInsert(session, items);
            }
        }
    }
}

Una búsqueda inicial de SO no es satisfactoria:

En la pregunta ¿Cómo hacer una llave duplicada EN ACTUALIZACIÓN en la primavera de datos JPA? que fue marcado como duplicado, me di cuenta de este comentario intrigante:introducir descripción de la imagen aquí

Eso era un callejón sin salida ya que realmente no entiendo el comentario, a pesar de que suene como una solución inteligente, y la mención de "misma instrucción SQL real".

Otro enfoque prometedor es la siguiente: Hibernate y primavera modificar consulta antes de someterse a DB

EN CONFLICTO no hacer nada / EN ACTUALIZACIÓN una llave duplicada

Tanto de las principales bases de datos de código de soporte abierto un mecanismo para empujar idempotencia abajo a la base de datos. Los ejemplos siguientes utilizan la sintaxis PostgreSQL, pero pueden ser fácilmente adaptados para MySQL.

Siguiendo las ideas de Hibernate y primavera modifico consulta antes de someterse a DB , enganchar en la generación de consultas de Hibernate , y ¿Cómo puedo configurar StatementInspector en Hibernate? , He implementado:

import org.hibernate.resource.jdbc.spi.StatementInspector;

@SuppressWarnings("serial")
public class IdempotentInspector implements StatementInspector {

    @Override
    public String inspect(String sql) {
        if(sql.startsWith("insert into rets")) {
            sql += " ON CONFLICT DO NOTHING";
        }
        return sql;
    }

}

con la propiedad

        <prop key="hibernate.session_factory.statement_inspector">com.myapp.IdempotentInspector</prop>

Por desgracia esto lleva a la siguiente mensaje de error cuando se encuentra un duplicado:

Causada por: org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException: actualización por lotes volvió recuento de filas inesperado de actualización [0]; real recuento de filas: 0; esperado: 1; excepción anidada es org.hibernate.StaleStateException: actualización por lotes volvió recuento de filas inesperado de actualización [0]; real recuento de filas: 0; esperada: 1

Lo cual tiene sentido, si se piensa en lo que está pasando debajo de las sábanas: la ON CONFLICT DO NOTHINGcausa cero filas que se insertan, pero se espera una inserción.

¿Hay una solución que permite inserciones concurrentes idempotent sin excepción compatibles con el proceso y no requiere definir manualmente toda la instrucción de inserción SQL para su ejecución por Hibernate?

Por si sirve de algo, siento que los enfoques que empujan hacia abajo el dupcheck a la base de datos son el camino a una solución adecuada.

ACLARACIÓN El IncomingItemobjetos consumida por el batchInsertmétodo de proceder de un sistema en el que los registros son inmutables. Bajo esta condición especial del ON CONFLICT DO NOTHINGcomporta igual que un UPSERT, a pesar de posibles pérdidas de la actualización enésimo .

Dragan Bozanovic:

Respuesta corta - Hibernate no lo soporta fuera de la caja (como se confirma por un gurú de hibernación en esta entrada del blog ). Probablemente se podría hacer que funcione en cierta medida en algunos escenarios con los mecanismos que ya se ha descrito, pero sólo el uso de consultas nativas mira directamente el enfoque más sencillo para mí para este propósito.

Respuesta larga sería que sería difícil para apoyarlo teniendo en cuenta todos los aspectos de la hibernación supongo, por ejemplo:

  • ¿Qué hacer con los casos para los que se encuentran duplicados, ya que se supone para convertirse logrado después de persistir? Fusionarlas en un contexto de persistencia?
  • ¿Qué hacer con las asociaciones que ya se han persistido, que las operaciones en cascada para aplicar en ellos (persistir / fusión / something_new; o es demasiado tarde en ese momento para tomar esa decisión)?
  • ¿Las bases de datos de información de retorno suficiente upsert operaciones para cubrir todos los casos de uso (saltado filas; claves generadas para no-omiten en el lote de inserción modos, etc).
  • ¿Qué pasa con @Auditlas entidades -ed, se les crea o se actualiza, si actualiza lo que ha cambiado?
  • O de versiones y bloqueo optimista (por la definición que realmente desea excepción en este caso)?

Incluso si Hibernate apoyado de alguna manera, no estoy seguro de que estaría utilizando esa característica si había demasiadas advertencias a tener en cuenta y tener en cuenta.

Por lo tanto, la regla general es que sigo:

  • Para los escenarios simples (que son la mayoría del tiempo): persistir + reintento. Reintentos en caso de errores específicos (por tipo de excepción o similares) se pueden configurar a nivel mundial con AOP-como enfoques (anotaciones, interceptores personalizados y similares) en función de los cuales los marcos que utiliza en su proyecto y es una práctica bien de todos modos, especialmente en entornos distribuidos .
  • Para los escenarios complejos y operaciones intensivas de rendimiento (sobre todo cuando se trata de procesamiento por lotes, consultas muy complejas y por igual): consultas nativas para maximizar la utilización de funciones de bases de datos específicas.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=171570&siteId=1
Recomendado
Clasificación