2012-04-16 21 views
5

Tenemos un marco de prueba utilizando JUnit, OpenEJB, Eclipselink y HSQLDB. Todo ha funcionado bien hasta el momento, y probar el nivel de servicio es muy sencillo. Ahora, sin embargo, estamos teniendo problemas al hacer importaciones masivas en una tabla (utilizando el nivel de servicio, administrador de entidades) o, por ejemplo, las entidades persistentes a una lista varias veces en un método de servicio.HSQLDB error de violación de clave primaria en pruebas JUnit

ESTA ES LA PARTE EXTRAÑA: Nuestras pruebas parecen romperse solo si las pruebas se ejecutan en una estación de trabajo suficientemente rápida desde la línea de comandos con Maven. Cuando ejecuto las pruebas a través de Eclipse IDE, todo está bien, pero a veces, al azar, también falla. Sospechamos que podría tener algo que ver con la velocidad con la que se ejecutan las pruebas, por extraño que parezca. La excepción es bastante simple porque básicamente nos dice que estamos tratando de agregar una entidad con una identificación ya existente. Hemos comprobado varias veces nuestros datos de prueba y la base de datos hsqldb. No hay filas preexistentes con identificadores que estamos tratando de usar. Todavía hsqldb arroja la excepción de clave primaria en algún momento. De nuestros registros podemos ver que la ID en conflicto no es siempre la misma, podría ser 300015 o 300008.

Estamos en el último momento aquí. ¿Podría tener algo que ver con las transacciones de HSQLDB o alguna otra cosa que provoque datos obsoletos?

Estamos utilizando HSQLDB 2.2.8, Eclipselink 2.3.0 y OpenEJB 4.0.0-beta2.

La relación que estamos tratando de agregar entidades a se asigna de la siguiente manera:

@OneToMany(mappedBy = "invoice", cascade = CascadeType.PERSIST) 
private List<InvoiceBalance> getInvoiceBalanceHistory() { 
    if (invoiceBalanceHistory == null) { 
     this.invoiceBalanceHistory = new ArrayList<InvoiceBalance>(); 
    } 
    return invoiceBalanceHistory; 
} 

La excepción raíz es:

Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: unique constraint or index violation; SYS_PK_10492 table: INVOICEBALANCE 
at org.hsqldb.jdbc.Util.sqlException(Unknown Source) 
at org.hsqldb.jdbc.Util.sqlException(Unknown Source) 
at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source) 
at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source) 
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105) 
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105) 
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:831) 
... 82 more 
Caused by: org.hsqldb.HsqlException: integrity constraint violation: unique constraint or index violation; SYS_PK_10492 table: INVOICEBALANCE 
at org.hsqldb.error.Error.error(Unknown Source) 
at org.hsqldb.Constraint.getException(Unknown Source) 
at org.hsqldb.index.IndexAVLMemory.insert(Unknown Source) 
at org.hsqldb.persist.RowStoreAVL.indexRow(Unknown Source) 
at org.hsqldb.TransactionManager2PL.addInsertAction(Unknown Source) 
at org.hsqldb.Session.addInsertAction(Unknown Source) 
at org.hsqldb.Table.insertSingleRow(Unknown Source) 
at org.hsqldb.StatementDML.insertSingleRow(Unknown Source) 
at org.hsqldb.StatementInsert.getResult(Unknown Source) 
at org.hsqldb.StatementDMQL.execute(Unknown Source) 
at org.hsqldb.Session.executeCompiledStatement(Unknown Source) 
at org.hsqldb.Session.execute(Unknown Source) 

EDIT:

me cambiaron la principal estrategia de generación de claves de GenerationType.AUTO (que parece usar la estrategia TABLE de forma predeterminada) para IDENTIDAD. Después de esto, nuestra masa persiste parece funcionar sin falta. Todavía no sé por qué HSQLDB "no está sincronizado" con la estrategia TABLE. No me gustaría cambiar nuestras entidades jpa solo porque nuestro framework de pruebas tiene errores :)

+0

¿Qué versión de HSQLDB estás usando? JUnit impone una pesada carga de retrotracción de transacciones en la base de datos y es probable que se encuentre con un error o un compromiso de ingeniería conocido para mantener HSQLDB rápido y pequeño. O tal vez una combinación de su configuración de base de datos y cómo Eclipselink configura la tabla de identidad y la administra. Siempre es una buena idea publicar números de versión. –

+0

Editaré los números de versión en la publicación original. Estoy utilizando HSQLDB 2.2.8 Eclipselink 2.3.0 y OpenEJB 4.0.0-beta2. Además, la Entidad no está utilizando una columna de IDENTIDAD, en cambio, la estrategia es AUTO, que creo que está utilizando la estrategia TABLE en HSQLDB. –

+0

¿Cuánto te importa encontrar el problema en lugar de evitarlo? El cambio de TABLE (o AUTO, que es TABLE para Eclipselink) a Sequence o IDENTITY probablemente eliminará el problema. Entender por qué está sucediendo implicará una gran cantidad de excavaciones dolorosas a través de la transacción y el aislamiento y la configuración de deshacer y errores de almacenamiento en caché, y así sucesivamente. –

Respuesta

0

Lo más probable es que se esté quedando sin memoria al importar muchas filas en una tabla MEMORY.

Debe aumentar la asignación de memoria o definir esta tabla en particular como una tabla CACHED.

Actualización: CACHED tablas se pueden utilizar en las bases de datos persistentes, no en todo-en-memoria de bases de datos:

CREATE CACHED TABLE mytable ... 

o para una tabla existente:

SET TABLE mytable TYPE CACHED 

ACTUALIZACIÓN:

Si esto no es causado por OOM, como lo confirma el cambio de la estrategia de generación, entonces parece que la estrategia de generación podría no estar incrementando el valor de la clave primaria generada en som e punto. La estrategia de identidad se basa en la base de datos para crear el valor generado, que funciona bien.

+0

Puede valer la pena intentarlo. ¿Cómo puedo definir una sola tabla como almacenada en caché en HSQLDB? –

+0

answere updated – fredt

+0

Cambié la estrategia de generación de claves principales de GenerationType.AUTO (que parece usar la estrategia TABLE de forma predeterminada) a IDENTIDAD. Después de esto, nuestra masa persiste parece funcionar sin falta. Todavía no sé por qué HSQLDB "no está sincronizado" con la estrategia TABLE. –

0

Es posible que su allocationSize esté definiendo un cuello de botella en plataformas relativamente rápidas u ocasionalmente. es decir, Cuando se establece de forma predeterminada en GenerationType.AUTO, que se vuelve defectuoso a la tabla, EclipseLink almacenará en caché el ID hasta el valor asignado. Luego buscará el generador para confirmar su último valor asignado. Si se realizó una búsqueda en el borde de allocationSize antes de que se guarde el siguiente conjunto de ID, puede encontrarse con una condición de carrera donde el enlace eclipse asigna la última identificación en la caché dos veces antes de actualizar la caché e intenta usar ambos para inserte y ambas inserciones fallan y son retiradas. Si puede usted debe comprobar para ver si esto sucede alrededor cuando el caché de asignación debe ser incrementado, pero tal vez ese tipo de verificación podría cambiar el comportamiento

+0

Curiosamente, aumentar el tamaño de asignación de 25 a, por ejemplo, 50 parece resolver el problema en las pruebas de JUnit, incluso cuando se crean cientos de entidades por prueba. Es una mierda hacer concesiones debido a las pruebas. – Kimi

0

Para integrity constraint violation: unique constraint or index violation Si eres un aficionado al depurador, puede reconstruir en hsqldb el modo de depuración y establece un punto de interrupción en org.hsqldb.index.IndexAVLMemory#insert en una línea donde se ha asignado a la variable compare una condición en el punto de interrupción compare == 0.

La fila defectuosa (la duplicada por ejemplo) será la que se pasa como argumento.

Cuestiones relacionadas