2 votes

Spring Data JPA avec la relation @OneToOne ne parvient pas à mettre à jour une entité déjà persistée.

Nous avons cette configuration : Un document possède une liste d'éléments et ces éléments sont des sous-classes d'Element, dans cet exemple Link. Un lien a une relation @OneToOne avec une ressource. Tout a fonctionné tant que nous avons créé l'ensemble du graphe d'objets en une seule étape.

Nous avons maintenant modifié notre code pour créer une instance de Resource, la faire persister et la transmettre à notre client (web, objets sérialisés en JSON). Le client crée une instance du document et ajoute un lien avec la ressource existante. Ensuite, nous essayons de persister le nouveau document.

À ce stade, l'opération échoue, car JPA tente de conserver la ressource existante avec le même identifiant. Je pense que cela se produit parce que la ressource est détachée et que JPA n'a aucun moyen de déterminer si cette entité doit être fusionnée ou persistée. Lorsque je supprime le CascadeType.ALL et que je le remplace par CascadeType.MERGE, tout se passe bien, sauf que la ressource n'est pas du tout mise à jour.

En principe, le processus est le suivant :

  • créer et conserver une ressource
  • opération de congé
  • démarrer la transaction
  • créer un document avec un lien et une ressource (détachée)
  • erreur

Y a-t-il un problème avec notre configuration JPA (voir nos entités) ? Je serais ravi de vous offrir une bière si vous pouviez me donner un indice... :-) TYIA.

Notre configuration est la suivante : EclipseLink 2.4.1, Spring Data JPA 1.3.0 (utilisant @Repository) avec PostgreSQL 9.2.

Document :

@Entity
public class Document implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @OrderColumn
    @JoinColumn(name = "document_id")
    private List<Element> elements = new ArrayList<Element>();

    // getters and setters
}

Élément :

@Entity
@Inheritance(strategy = InheritanceType.
public class Element implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "document_id", insertable = false, updatable = false)
private Document document;

    // getters and setters
}

Lien :

@Entity
public class Link extends Element {

    @Column
    private String appearance;

    @Column
    private String referenceType;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private Resource resource;

    // getters and setters
}

Ressource :

@Entity
public class Resource {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column
    private String uri;

    // getters and setters
}

1voto

frant.hartm Points 4634

Le type CascadeType.MERGE est destiné aux opérations de fusion en cascade, par exemple :

Document document = getDetachedDocumentSomehow();
em.merge(document)

pour fusionner l'ensemble du graphe d'objets avec le contexte de persistance.

Ce que vous devez faire, c'est fusionner votre instance de ressource détachée dans le contexte de persistance de votre deuxième transaction.

Resource resource = getDetachedResourceSomehow();
mergedResource = em.merge(resource)
link.setResource(mergedResource)

EDIT : Sans utiliser le gestionnaire d'entités :

Resource resource = getDetachedResourceSomehow();
managedResource = resourceDao.findOne(resource.getId)
link.setResource(managedResource)

L'essentiel est que vous devez définir une instance gérée de Resource.

0voto

s.froehlich Points 321

Je pense que j'ai déclenché un bogue dans l'implémentation d'EclipseLink. Pour les personnes qui rencontrent les mêmes problèmes, voici quelques liens utiles :

http://chandanpandey.com/2012/12/22/persisting-a-detached-entity-in-jpa https://bugs.eclipse.org/bugs/show_bug.cgi?id=324941

La solution a consisté à interdire l'utilisation du type CascadeType.ALL

@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)

et utiliser à la place

@OneToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.DETACH}, orphanRemoval = false)

et désactiver la suppression des orphelins. Cela a certainement quelques inconvénients et j'espère que cela sera corrigé dans une prochaine version d'EclipseLink (ce dont je doute car le rapport de bogue date de 2010).

J'espère que cela aidera quelqu'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X