with manytomany many ejemplo column java hibernate jpa orm hibernate-mapping

java - manytomany - jpa many to many with extra column



JPA clear collection y agrega nuevos elementos (5)

Tengo una colección @OneToMany (lista) que me gustaría borrar y agregar nuevos elementos en la misma transacción.

Utilizando

collection.clear(); collection.add(new EntityB());

Simplemente agrega la nueva instancia y nunca elimina nada. Tengo orphanRemoval = true para el campo de colección.

ADICIONAL:

// Parent entity @OneToMany(mappedBy = "product", orphanRemoval = true) private List<Feature> features = new ArrayList<>(); // Child entity @ManyToOne(cascade = CascadeType.ALL) private Product product; // Clear and add attempt product.getFeatures().clear(); Feature feature = new Feature(product, ls); product.getFeatures().add(feature);


En la Sección 2.9, Relaciones entre entidades, la especificación de JPA 2.1 dice:

Si la entidad que se deja huérfana es una entidad separada, nueva o eliminada, la semántica de orphanRemoval no se aplica.

¿Estás seguro de que tu entidad se gestiona en el contexto de persistencia al eliminarla de la colección?

Puede solucionarlo utilizando fetch=fetchType.EAGER o fetch joins . Alternativamente (depende de su caso de uso), puede ser suficiente configurar la opción de cascade adecuada.


Resulta que la solución real estaba usando una anotación @JoinColumn en lugar del parámetro mappedBy = "".


Esto realmente parece ser un error en muchas versiones de Hibernate. Lo probé con EclipseLink y funciona allí sin problemas.

Como solución temporal en Hibernate (probado en Hibernate 4.3.6-Final): CascadeType.PERSIST cualquier cascada en la entidad Feature y agregue CascadeType.PERSIST (o CascadeType.ALL ) en la entidad Product .

Solo para asegurarse de que no funciona, intente lo siguiente:

EntityManager em = ...//fetch the entitymanager. If a Container-managed transaction, you already got it injected em.getTransaction().begin();//only if resource-local persistence unit. Otherwise if JTA: open the transaction the JTA-specific way (if that was not already done by the container) Product product = em.find(Product.class, productId); for (Feature crtFeature : product.getFeatures()) { if (!em.contains(crtFeature)) { throw new RuntimeException("Feature is not managed, so removeOrpahns cannot work"); } } product.getFeatures().clear(); Feature feature = new Feature(product, ls); em.persist(feature);//you need this, as there is no cascading from Product to Feature. product.getFeatures().add(feature); em.getTransaction().commit();//if you work with a resource-local persistence unit. Otherwise if JTA: commit the transaction the JTA-specific way (if that was not already done by the container)


Intenta borrar solo un lado de la asociación bidireccional.

Entonces, en lugar de:

collection.clear();

Intenta borrar ambos lados y debería funcionar:

for(Iterator<Feature> featureIterator = features.iterator(); featureIterator.hasNext(); ) { Feature feature = featureIterator .next(); feature.setProduct(null); featureIterator.remove(); }

Además, elimine la cascada de @ManyToOne y @OneToMany a @OneToMany .

Tenga en cuenta las limitaciones únicas

Sin embargo, si tiene una restricción única, este clear + add Anti-Patrón no funcionará ya que la acción INSERTAR se ejecuta antes de ELIMINAR uno como se explica en este artículo .

La forma correcta de hacerlo es verificar qué entradas se deben eliminar y simplemente eliminarlas. Luego, agregue los nuevos y actualice los que fueron modificados. Así es como haces una fusión de colecciones correctamente.


Me enfrenté a un problema similar recientemente. Para mí, el problema era que todavía se hacía referencia a los huérfanos desde otra entidad gestionada, y había una cascada PERSIST definida para esa relación:

// Parent entity @OneToMany(mappedBy = "product", orphanRemoval = true) private List<Feature> features = new ArrayList<>(); // Child entity @ManyToOne private Product product; @ManyToOne private Description description; // Another entity (let''s say descriptions can be shared between features) @OneToMany(mappedBy = "description", cascade = CascadeType.PERSIST) private List<Feature> features = new ArrayList<>();

Supongamos que todas las entidades involucradas se gestionan, es decir, se cargan en el contexto de persistencia. Ahora hacemos lo mismo que el OP:

// Clear and add attempt product.getFeatures().clear(); Feature feature = new Feature(product, ls); product.getFeatures().add(feature);

Filosóficamente, el problema aquí es que el modelo de objetos se vuelve inconsistente si solo se elimina la Característica de la entidad Producto, pero no de la entidad Descripción. Después de todo, desea que se elimine la característica, pero todavía se hace referencia a ella desde otros objetos. Técnicamente, lo que sucede es que hay dos cascadings conflictivos que van a la entidad Feature, y el resultado puede depender del orden en que se apliquen.

Como la característica se eliminó de la colección en el producto, se aplica la eliminación huérfana, y la entidad de entidad pasa al estado "eliminado" durante la próxima descarga, como se indica en la especificación de JPA 2.1 (2.9). Agregué énfasis en las partes relevantes:

Las asociaciones que se especifican como OneToOne o OneToMany admiten el uso de la opción orphanRemoval. Los siguientes comportamientos se aplican cuando huérfano Remoción está en vigor:

  • Si una entidad que es el objetivo de la relación se elimina de la relación (estableciendo la relación como nula o eliminando la entidad de la recopilación de relaciones), la operación de eliminación se aplicará a la entidad que se está quedando huérfana. La operación de eliminación se aplica en el momento de la operación de descarga . La funcionalidad huérfano de remoción está destinada a entidades que son "propiedad" privada de su entidad matriz. De lo contrario, las aplicaciones portátiles no deben depender de un orden específico de eliminación, y no deben reasignar a una entidad que ha quedado huérfana a otra relación o tratar de persistir de otra manera . Si la entidad que se deja huérfana es una entidad separada, nueva o eliminada, la semántica de orphanRemoval no se aplica.

Sin embargo, la misma Característica aún se referencia desde una entidad de Descripción, que tiene un PERSIST en cascada hacia la Característica. La especificación de JPA 2.1 dice lo siguiente:

La semántica de la operación de descarga, aplicada a una entidad X es la siguiente:

  • Si X es una entidad gestionada, se sincroniza con la base de datos.

    • Para todas las entidades Y referenciadas por una relación de X, si la relación con Y se ha anotado con el valor del elemento de cascada cascada = PERSIST o cascada = TODO, la operación de persistencia se aplica a Y.

Entonces, esta cascada realizará una operación "persistir" en la entidad de entidad, incluso si no llamamos a em.persist () en la descripción. Es suficiente para que la Descripción se administre cuando se realiza una descarga para activar esta cascada persistente.

Esto significa que estamos haciendo exactamente lo que las especificaciones nos dijeron que no deberíamos: realizar la eliminación de huérfanos y continuar en la misma entidad. Lo que parece suceder en la práctica en Hibernate es que ambas operaciones se aplican sucesivamente. Primero, la operación de eliminación hace que la entidad Feature pase al estado "eliminado", luego la operación persist convierte a la entidad eliminada en una entidad administrada. Como resultado, la característica no se elimina de la base de datos.