Pregunta

Estoy usando JPA con Hibernate debajo, y tengo problemas para hacer que la fusión funcione, pero antes de describir los problemas que me encuentro con JPA, permítanme exponer lo que estoy tratando de lograr, en caso de que mis problemas se deriven de mi enfoque.

Tengo datos del sistema que necesito colocar en otro sistema. Para hacer esto, estoy leyendo los datos y luego construyendo nuevos objetos ORM basados ??en esos datos, luego deseo conservar los objetos ORM en la base de datos. Ahora, cuando la base de datos está vacía, el programa funciona sin problemas, con una simple llamada em.persist (objeto). Sin embargo, quiero poder ejecutar este proceso cuando la base de datos tenga datos, agregue nuevos datos y actualice los datos antiguos según sea necesario. Aquí es donde tengo problemas.

Realizo una comprobación simple para ver si el elemento ya existe en la base de datos, si no se encuentra nada, persisto, si existe el registro, intento una combinación. Lo que falla con un error de registro duplicado;

ERROR JDBCExceptionReporter - Violation of UNIQUE KEY constraint 'UK-SubStuff-StuffId-SubStuffNumber'. Cannot insert duplicate key in object 'SubStuff'.

Me parece extraño que la llamada em.merge () esté intentando una inserción en lugar de una actualización (confirmé esto a través del registro de SQL).

Hibernate: insert into SubStuff (SubStuffNumber, StuffId, Name, TypeId) values (?, ?, ?, ?)

Debo tener en cuenta que estoy usando objetos que caen en cascada en subobjetos. La falla se está produciendo en un subobjeto, lo que tiene sentido para mí, ya que espero que intente fusionar los subobjetos primero.

A continuación se encuentra mi código. Disculpe el estado aproximado. He intentado documentar algunos enfoques alternativos aquí.

private void storeData(Collection<Stuff> Stuffs) {

    for (Stuff stuff : Stuffs) {
        //I think this first block can be safely ignored, as I am having no issues with it
        //  Left it in just in case someone more experianced then I sees the root of the issue here.
        Collection<SubStuff> subStuffs = stuff.getSubStuffCollection();
        for (SubStuff s : subStuffs) {
            //Persist SubStuff Type, which DOES NOT cascade,
            //  due to it not having an internal SubStuff collection
            Query q = em.createNamedQuery("SubStuffType.findByType");
            q.setParameter("type", f.getTypeId().getType());
            try {
                SubStuffType sst = (SubStuffType) q.getSingleResult();
                s.setTypeId(sst);
            } catch (NoResultException ex) {
                if (logger.isDebugEnabled()) logger.debug("SubStuff Type not found, persisting");
                em.persist(s.getTypeId());
            }
        }

        if (em.find(Stuff.class, stuff.getId()) == null) {
            //Persist on Stuffs will cascade to SubStuffs
            em.persist(stuff);
        } else {
            //  Failing to merge SubStuff, tries to insert duplicate
            //  Merge SubStuff first
            // The block below is my attempt to merge the SubStuff Collection before merging Stuff,
            //  it creates the same isuse as a straight merge of Stuff.
            Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
            for (SubStuff s : SubStuffs) {
                Query q = em.createNamedQuery("SubStuff.findBySubStuffNumberStuffId");
                q.setParameter("SubStuffNumber", s.getSubStuffNumber());
                q.setParameter("StuffId", stuff.getId());
                try {
                    SubStuff subStuff = (SubStuff) q.getSingleResult();
        // -----> Merge fails, with an duplicate insert error
                    SubStuff mergedSubStuff = em.merge(s);
                    mergedSubStuffs.add(mergedSubStuff);
                } catch (NoResultException ex) {
                    throw ex;
                }
            }
            stuff.setSubStuffCollection(mergedSubStuffs);

        // -----> This will fails with same error as above, if I remove the attempt
            //  to merge the sub objects
            em.merge(stuff);
        }
    }
}

Si alguien con experiencia en JPA puede ayudarme, realmente lo apreciaría. Las diferencias entre saveOrUpdate () de Hibernate y merge () de JPA obviamente me están fallando, pero a pesar de leer varios artículos sobre la fusión de EnityManger, todavía no puedo comprender lo que está pasando aquí.

Gracias por tu tiempo.

¿Fue útil?

Solución

La maldición de StackOverflow ha golpeado de nuevo. Después de trabajar en este problema durante aproximadamente un día, decido publicar esta pregunta, en 20 minutos tuve un momento eureka y lo resolví. En parte porque había aclarado mis pensamientos lo suficiente como para publicar la pregunta. Al pensar en qué información podría ser pertinente a la pregunta, me di cuenta de que mis claves generadas automáticamente eran las culpables (o mi correcto, mi estúpido no manejo de ellas en una combinación).

Mi problema es que SubStuff (esquema de nomenclatura del peor sustituto, perdón por eso) tiene una clave primaria artificial autogenerada. Así que cuando me fusioné tenía que hacer;

SubStuff subStuff = (SubStuff) q.getSingleResult();
//++++++++
s.setId(subStuff.getId());
//++++++++

//The following code can be removed.
//--- SubStuff mergedSubStuff = em.merge(f);
//--- mergedSubStuffs.add(mergedSubStuff);

Esto establece la clave principal en la fila que ya existe en la base de datos y en la prueba inicial parece funcionar bien.

La llamada a fusionar se puede simplificar solo a la llamada a em.merge (material), ya que los objetos de subestación en cascada y la colección mergedsubstuff pueden eliminarse todos juntos ya que ya no estamos haciendo una fusión dentro de ese bucle.

Gracias a cualquiera que haya leído mi pregunta ridículamente larga, espero que mi pregunta sea útil para alguien en el futuro.

Otros consejos

Su problema es con el siguiente par de líneas:

Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
...
stuff.setSubStuffCollection(mergedSubStuffs);

JPA considerará la nueva colección como un nuevo conjunto completo de sub-entidades y siempre las insertará como tales. Sigue trabajando con la colección original de SubStuff en la entidad Stuff y estarás bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top