12
votes

Changer le type d'une entité préservant son identifiant

J'utilise Hibernate comme couche de persistance. Il existe 2 entités qui vivent dans la même table extension d'une superclasse avec une stratégie d'héritage de table unique.

org.hibernate.PersistentObjectException: detached entity passed to persist: C


5 commentaires

J'ai ajouté l'exception à la question


Juste un hunch, mais avez-vous essayé d'appeler fusionné () au lieu de persister ()?


Ouais, la fusion donne C un nouvel identifiant.


Cela peut être un exemple de l'insuffisance de l'API d'EntityManager. Ce n'est pas aussi expressif que l'API d'hibernation indigène et fait parfois un peu moins de sens.


Y a-t-il un moyen de le faire dans l'API d'Hibernate indigène?


5 Réponses :


2
votes

Dans ce cas, "C" est un objet que la session hibernate ne sait rien, mais il a un identifiant, il suppose donc que l'objet a déjà été persisté. Dans ce contexte, persist () n'a aucun sens, et il échoue donc.

Le Javadoc pour hibernate session.persist () (Je sais que vous n'utilisez pas l'API hibernate, mais la sémantique est la même, et les documents hibernés sont meilleurs) dit "faire une instance transitoire persistante". Si votre objet a déjà un identifiant, ce n'est pas transitoire. Au lieu de cela, il pense que c'est une instance détachée (c'est-à-dire une instance qui a été persistée, mais n'est pas associée à la session en cours).

Je vous suggère d'essayer de fusionner () au lieu de persister ().


1 commentaires

Merci d'avoir expliqué la raison de l'exception, j'y dérangerai. La fusion n'a pas échoué, mais donne un nouvel identifiant, ignorant ce qui a été défini.



0
votes

Vous pouvez utiliser votre propre identifiant (non généré) et procéder à ce qui suit:

  1. revenir b
  2. transaction ouverte
  3. Supprimer B
  4. commettre la transaction
  5. Ouvrez une nouvelle transaction
  6. créer c et persistez-le
  7. Fermer la deuxième transaction

    De cette façon, vous effacerez l'identifiant de la table avant de la réinsérer comme c.


1 commentaires

Ce serait assez difficile à mettre en œuvre. Toutes mes entités utilisent des identifiants auto-interprétés. Existe-t-il un moyen de forcer la valeur d'un identifiant généré?



0
votes

Comment faites-vous la distinction entre les deux entités de la table? Je suppose qu'il y a une valeur de champ (ou des valeurs) que vous pouvez changer pour faire B dans un C?

Vous pouvez avoir une méthode où vous chargez la super-classe A et modifier les valeurs distinctives et enregistrer. Ensuite, dans votre prochaine session hibernate, votre B sera un c.


1 commentaires

Hibernate utilise une colonne discriminatoire pour déterminer quelle classe il doit instancier lors du chargement des données. La valeur de la colonne est égale au nom simple de la classe. Vous suggérez-vous d'utiliser SQLL SQLL pour modifier les données de la table? Je ne vois pas une autre façon de faire ce que vous suggérez.



0
votes

Je pense que Skaffman est juste ici, une fois que l'identifiant a été défini, il ne persiste pas et que l'ID est généré, il s'attend à ce que la séquence soit chargée de l'attribution du numéro d'identification.

Vous ne pouvez éventuellement pas mettre l'identifiant comme @GeneratedValue? ou l'un des différents types de stratégie génératrice peut-être éviter la fusion générant une nouvelle valeur de séquence, mais je soupçonne que cela serait problématique.


0 commentaires

16
votes

Hibernate tente de rendre la persistance aussi transparente que possible, ce qui signifie qu'il essaie de suivre les mêmes principes que les objets Java normaux. Maintenant, reformulez votre question en Java, vous obtiendrez:

Comment puis-je convertir une instance de classe B en une instance de classe C (incompatible) C?

Et vous connaissez la réponse à cela - vous ne pouvez pas. Vous pouvez créer une nouvelle instance nouvelle de C et copier les attributs nécessaires, mais b sera toujours Be B, jamais C. Ainsi, la réponse à votre question initiale est - elle ne peut pas être faite via l'API JPA ou Hibernate.

Cependant, contrairement à Java uni, avec hibernate, vous pouvez tricher :-) inheritanceType.single_table_table est mappé en utilisant @discriminatorcolumn et afin de convertir B en C, vous devez Mettez à jour sa valeur de tout ce qui est spécifié pour B dans tout ce qui est spécifié pour C. L'astuce est - vous ne pouvez pas le faire en utilisant l'API Hibernate; Vous devez le faire via SQL plaine. Vous pouvez toutefois planter cette instruction de mise à jour comme requête nommée SQL et l'exécuter à l'aide d'installations Hibernate.

L'algorithme est donc:

  1. expulsion B de la session si elle est là (c'est important)
  2. Exécutez votre requête nommée.
  3. Chargez ce qui est-maintenant-connu-connais-c utilisant l'ID de l'ancien B.
  4. Attributs de mise à jour / SET si nécessaire.
  5. persist C

0 commentaires