Buscar
Social
Ofertas laborales ES

Foro sobre Java EE > Inserción no deseada por la clase JPAController generada por NetBeans

Buenas,

Me estoy encontrando un error raro en una aplicación de escritorio que usa JPA. En un formulario hecho por mí (sin asistentes) con una funcionalidad CRUD maestro-detalle, en el manejador de eventos para el botón Guardar-tras-editar de la entidad maestra (GlsEntry), tengo esto:


// Get a reference to the JPA-managed entity
GlsEntry ge = glosEntryTableModel.getElement(index);
// Update data in the above entity, without modifying in any way the detail collection
ge.setTerm(glseTermField.getText());
ge.set(...)
try {
gec.edit(ge); // "gec" is the NetBeans generated JPAController class for GlsEntry entity
glosEntryTableModel.fireTableRowsUpdated(index, index);
} catch (Exception ex) {
Logger.getLogger(GlsEntryGuiManager.class.getName()).log(Level.SEVERE, null, ex);
statusBar.logMessage(JStatusBar.LogMsgType.ERROR, "Error while saving entry changes",
"Error while saving glossary entry changes", ex);
}

Aquí hay una captura de pantalla del formulario:

http://postimg.org/image/4d9uom2uh/

Ahí eliges "Assistive technologies" en la tabla de la izquierda, se rellenan los campos bajo la tabla, cambias "OTHER" a "NOUN" y pulsas "Save & new trans" (ignorad el literal "new trans."; sólo guarda la GlsEntry usando el código de arriba). Esto funciona bien, pero tan pronto como elijo otra GlsEntry en la tabla de la izquierda, obtengo esta excepción:

Internal Exception: java.sql.SQLIntegrityConstraintViolationException: (reversed translated from Spanish message) The sentence has terminated abnormally because it would otherwise caused a duplicated key value in a primary or unique key constraint in a unique index identified as 'GLSTRANSLATION_PK' defined in 'GLSTRANSLATION'.
Error Code: 20000
Call: INSERT INTO APP.GLSTRANSLATION (ID, GLSTCOMMENT, GLSTCREATIONDATE, GLSTLASTUPDATE, GLSTVALUE, GLSE_ID, L10N_ID) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [46, null, 2014-08-03 19:43:05.033, 2014-08-03 19:43:05.033, tecnologías adaptadas a usuarios con necesidades especiales, 60, 2]

Como puede verse, de alguna manera está tratando de guardar la entrada de detalle mostrada previamente en la tabla derecha, aunque no fue modificada ni borrada. El manejador de elementChanged en la tabla de la izquierda es:


public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
int selectedRow = glseTable.getSelectedRow();
if (selectedRow != -1) {
selectedRow = glseTable.convertRowIndexToModel(selectedRow);
glseTermField.setText(glseTable.getValueAt(selectedRow,
GlosEntryTableModel.COLUMN_HEADER_TERM).toString());
// Fill other UI fields
refreshGlsTranslationList();
(...)
}

y


private void refreshGlsTranslationList() {
int selectedRow = glseTable.getSelectedRow();
L10n l = (L10n) localeSelCombo.getSelectedItem();
TypedQuery<GlsTranslation> glstQuery = entityManager.createNamedQuery("GlsTranslation.findByEntryAndLocale",
GlsTranslation.class);
glosTranslationTableModel.clearAll();

if ((selectedRow != -1) && (l != null)) {
selectedRow = glseTable.convertRowIndexToModel(selectedRow);
GlsEntry ge = glosEntryTableModel.getElement(selectedRow);
glstQuery.setParameter("glseid", ge);
glstQuery.setParameter("l10nid", l);
glosTranslationTableModel.addAll(glstQuery.getResultList()); // Esta línea causa la excepción
clearTranslationDetailFields();
}
}

Es ahí donde se lanza la excepción. Todo el código de arriba es sólo para demostrar que no estoy intentando insertar el registro duplicado, así que debe venir de la clase GlsEntryJPAController generada por NetBeans.

Intuyo que puede tener algo que ver con el hecho de que hay dos instancias de EntityManager simultáneamente. Una se usa por la clase del formulario maestro-detalle y la otra se crea por el JPAController (el constructor JPAController requiere un EntityManagerFactory, pero crea su propio EntityManager interno). Pero el problema real es que el método edit() hace un montón de cosas con la colección de GlsTranslation de la instancia de GlsEntry, y pienso que termina enviando el mensaje equivocado al EntityManager del formulario. La clase JPAContoller no ha sido modificada por mí, por lo que todo su código es el generado automáticamente.

Llevo poco tiempo usando usando JPA y las clases de controlador JPA generadas por NetBeans. ¿Alguien que tenga más experiencia puede darme alguna pista sobre si estoy usando mal el controlador, o quizá es que he encontrado un bug?

agosto 6, 2014 | Registered Commenterrickiees

Tras alguna prueba confirmé que, efectivamente, el problema lo generaba la clase JPAController creada por el asistente de NetBeans, porque se vuelve paranoica intentando comprobar si los hijos de la clase que controla son los mismos en el objeto en memoria que en la base de datos, y acaba intentando reinsertarlos, lo que provocaba el error. Además, al usar un EntityManager propio, facilitaba que sucediera esto. Al final opté por prescindir de ella.

Tendré que hacer alguna similar, sólo por ordenar el código un poco, que permita pasarle el EntityManager y que tenga métodos de actualización distintos dependiendo de si el cambio en una entidad afecta a sus hijos o no. Por ejemplo, si tienes un objeto de la clase Factura con una lista de hijos de la clase LineaDetalle, cambiar la propiedad formaDePago en el objeto factura no debería trastearse con las líneas de detalle que cuelgan de él.

Aun así, si alguien se ha encontrado el mismo problema y lo solucionó directamente sobre las clases generadas por NetBeans, estaría bien que lo comentara.

agosto 8, 2014 | Registered Commenterrickiees

Lleva esto algo de años pero resulta una solución que encontré fue quitar todas las llaves foraneas de mis tablas y con eso ya funcionan todos los metodos del JPA controlador

septiembre 17, 2016 | Unregistered CommenterEPRO