7

Mi proyecto está utilizando hibernate con spring transaction manager y mi base de datos es postgres (podría ser irrelevante).manejo de la restricción de base de datos hibernate

Estoy tratando de leer grandes archivos xml y construir objetos a partir de ellos (los objetos no son grandes, pero la cantidad es) e insertarlos en la base de datos.

Si, por casualidad, uno de mis objetos infringe la restricción de la base de datos, todo el proceso se detiene. ¿Cómo puedo omitir los que violan la restricción de la base de datos? alternativamente, ingrese su identificación o lo que sea a un archivo de registro?

actualización Pregunta:

He estado navegando a través SO y se encontró que para las inserciones lotes se recomienda mejor usar sesión sin estado, pero sigo teniendo el mismo problema y el inserto se detiene:

May 26, 2012 4:45:47 PM org.hibernate.util.JDBCExceptionReporter logExceptions 
SEVERE: ERROR: duplicate key value violates unique constraint "UN_FK" 
    Detail: Key (fid)=(H1) already exists. 

Aquí están las partes relevantes de mi código para analizar xml e insertar en db, para simplificar, supongamos que estoy insertando películas:

//class field 
@Autowired 
private SessionFactory sessionFactory; 

@Override 
public void startDocument() throws SAXException { 
    session = sessionFactory.getCurrentSession(); 
} 

@Override 
public void endElement(String uri, String localName, String qName) throws SAXException { 
if (qName.equalsIgnoreCase("FILM")) { 
     movie.setCategory(category); 
     movie.setAdded(new Date()); 
     session.insert(movie); 
    } 
} 

I y tengo esta propiedad configurada en la aplicación-ctx hibernate.jdbc.batch_size a 100. ¿Es realmente necesario seleccionar antes de insertar para evitar esto?

Actualización 2:

Si uso StatelessSession en lugar de la sesión, me sale cerca de 20 insertos y de las paradas de procesamiento indefinidamente sin excepción alguna ni nada.

Supongo que el número 20 se debe a que estoy agrupando conexiones con tomcat y tengo maxActive="20".

Bounty Actualización:

me encantaría ver a alguien solución de oferta (sin defensiva seleccione si es posible). Usando statelessSession o solo sesión.

Respuesta

4

La mayoría de los tipos de restricciones, como si una columna es anulable o tiene un ancho máximo, puede verificar usando Hibernate Validator. Simplemente ejecute manualmente la validación en el objeto antes de intentar persistir.

Para algunas cosas, particularmente las restricciones únicas, debe ejecutar una selección "defensiva" para ver si existe una colisión, o mantener un conjunto de valores en memoria ya insertados.

2

Para insertar una gran cantidad de objetos de un archivo xml, debe considerar usar lotes de primavera. El parámetro skip-limit le permite indicar cuántas líneas erróneas puede tener en su xml antes de detener el proceso por lotes. Verifique también skip-policy y skippable-exception; seee Configuring a Step en la documentación de Spring.

Si no desea utilizar un lote de muelles, solo use un simple try catch que permita que su proceso continúe hasta el final.

+0

Una declaración de intento de captura simple no es suficiente. Una vez que Hibernate lanza una excepción, el estado de la sesión es incoherente, la transacción debe revertirse y la sesión se cierra. Además, la excepción solo se producirá en el momento del enjuague, mucho después de que se haya conservado el registro defectuoso. –

+0

Es por eso que se recomienda utilizar una sesión de hibernación sin estado para tales casos. Evitará el estado incoherente y también reducirá el consumo de memoria (o no tendrá que expulsar a las entidades ya tratadas) –

1

Creo que Affe tiene el enfoque correcto con una selección defensiva antes de insertar.

Puede considerar el uso de savepoint antes de cada inserción y volver al punto de guardado si se produce una excepción. Habrá una sobrecarga de rendimiento al crear un punto de rescate. El punto de rescate deberá ser liberado cuando hayas terminado.

Ver AbstractTransactionStatus.setSavepoint() o si tiene acceso a la agrupación de conexiones JDBC setSavepointrollback(Savepoint) y releaseSavepoint(Savepoint)

2

¿Por qué cree que todo esto tiene que ser una gran transacción? Todo lo que describes de hecho implica para mí que de hecho tienes numerosas transacciones aquí. Para los objetos que "salieron por error", solo desaloja esa entidad y revierte la transacción. Se vuelve un poco más complejo si el elemento "FILM" define un gráfico de objetos, pero la idea es la misma.

4

Creo que es imposible validar algo tan completamente que pueda garantizar una inserción exitosa. En algunos casos, sin importar lo que hagas, alguien más puede insertar algo en el DB entre la validación y la inserción, lo que causa una violación de la restricción.

En la mayoría de los casos, solo recomendaría manejar la excepción como cualquier otra.

0

Aunque es una vieja pregunta, me enfrenté a una situación similar recientemente y esto es lo que hice.

He usado sesiones sin estado también, pero esto es lo que hice diferente. Publiqué una session.insert que tiene éxito o falla debido a la violación de Restricción, en la violación de Restricción se genera una RestraceptionViolationException, al detectar que le daría el objeto que falló la inserción, haga lo que quiera con el objeto fallido. Junto con las transacciones también se usaron puntos guardados para cada inserción (los puntos guardados son más baratos y no causan un gran impacto en el rendimiento), por lo que cuando una transacción falla debido a algún problema, la confirmación se realiza hasta el último punto guardado (cláusula catch).

mi código parecía algo como esto:

try { 
    session.connection().setSavePoint("lastSaved"); 
    session.insert(obj); 
} 
catch(ConstraintViolationException) { 
    log.error(obj.getUserId()); 
    .... 
} 
tx.commit(); 
.... 
catch(TransactionException e) { 
    tx.rollback(); 
} 
Cuestiones relacionadas