J'ai deux tables, b
et a
:
a
a une clé étrangère vers b
qui définit cette relation un
, et un JPA @ID
b
associé lorsque a
est supprimé b_id
de a
est NOT NULL
Le problème est que lorsque je supprime mon objet A
avec le référentiel JPA, j'obtiens une ConstraintViolationException
sur sa clé étrangère.
Je m'attendrais à ce que les lignes a
et b
soient supprimées (en commençant intelligemment par celle de a
).
Comment pourrais-je contourner ce problème en sachant que je veux conserver:
a
vers b
b
étant le JPA @Id
pour a
@Entity @Table(name = "b") public class B { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(name = "dbid") private Integer id; @OneToOne(mappedBy = "b") private A a; }
@Entity @Table(name = "a") public class A { @Id @Column(name = "b_id") @GeneratedValue(generator = "gen") @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name="property", value="b")) private Integer bId; @OneToOne(cascade = CascadeType.REMOVE) @PrimaryKeyJoinColumn private B b; }
CREATE TABLE `b` ( `dbid` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`dbid`), ); CREATE TABLE `a` ( `b_id` int(11) NOT NULL, KEY `b_fk` (`b_id`), CONSTRAINT `b_fk` FOREIGN KEY (`b_id`) REFERENCES `b` (`dbid`), );
[EDIT] Après toutes les discussions en réponse aux commentaires et relecture de ma question, les propositions avec orphanRemoval
sont en effet dans la portée et le travail.
5 Réponses :
Pouvez-vous essayer dans la classe B d'ajouter le
@OneToOne(mappedBy = "b", cascade = CascadeType.REMOVE) private A a;
De plus, si dans la base de données vous n'avez qu'une clé étrangère "a a une clé étrangère vers b" pouvez-vous également faire une clé étrangère de b à a également.
Peut-être que le texte de ma question n'était pas assez clair, je l'ai juste édité, mais je veux la suppression en cascade de B à A et non l'inverse. Je ne veux pas non plus modifier mon schéma. Merci pour l'intérêt.
Désolé, mon erreur dans le commentaire ci-dessus, je veux vraiment une cascade de A
à B
, ce qui signifie que la suppression d'un A
devrait supprimer le A
associé code> B .
essayez avec le code ci-dessous -
@OneToOne(mappedBy = "b",cascade = CascadeType.ALL,fetch = FetchType.LAZY,orphanRemoval=true ) private A a;
Non, relisez la question, je veux la suppression en cascade de a à b
et orphanRemoval
est hors de propos.
dans la réponse ci-dessous, vous avez répondu exactement le contraire. pourriez-vous poster le code de suppression.
lisez également le lien concernant l'utilisation de Cascade.Remove
et ophanRemoval
. Vous devrez peut-être utiliser orphanRemoval
selon la manière dont vous effectuez l'opération de suppression.
- orphanRemoval
est très clair pour moi. Certains de mes B
peuvent exister sans A
, donc je ne veux pas orphanRemoval. - dans l'autre réponse, j'ai fait une erreur dans mon commentaire, le texte de ma question est valide, je souhaite une suppression en cascade de A
à B
, c'est-à-dire que la suppression d'un A
doit supprimer le B
associé
Je crois qu'avec votre code actuel, vous ne créez pas de valeurs dans le tableau A, si vous persistez les données dans le tableau B. Maintenant que vous venez à votre problème, vous pouvez toujours réaliser ce dont vous avez besoin en utilisant la propriété orphanRemoval
à la table La fin de A car elle ne supprimera que ce dont vous avez besoin. Il supprimera uniquement les données associées du tableau A du tableau B.Le tableau B peut toujours avoir ses propres données car nous ne faisons pas de nouvelles entrées dans le tableau A.
Si vous souhaitez supprimer l'objet de B
, chaque fois que le A
associé est supprimé (c'est le quatrième point de votre wishlist:
Je souhaite une suppression en cascade qui supprime le
b
associé lorsquea
est supprimé
alors vous devez changer votre mappage dans A
en:
@OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true) @PrimaryKeyJoinColumn private B b;
Comme déjà indiqué dans la réponse de @ Narendra, ce n'est pas ce que je veux, certains de mes B peuvent exister sans A, donc je ne veux pas de orphanRemoval
. De plus, cela ne résout pas le problème principal qu'est la levée de ConstraintViolationException
.
Est-ce que je comprends clairement - vous ne voulez pas ce que vous avez spécifié dans votre question?
@Andronicus, votre solution est correcte. La seule chose qui manque est ON DELETE CASCADE dans le tableau a.
@PierreMardon s'il vous plaît, acceptez une réponse à votre question et ajoutez-en une autre avec la bonne spécification, vous ne faites que confondre les autres.
Il n'y a pas de bonne réponse à ma question pour le moment, peut-être celle d'Alan Hay que j'essaierai bientôt. Vous supposez que orphanRemoval
est applicable mais ce comportement très spécifique est complètement hors de la portée de cette question.
Je suis désolé pour ma propre confusion, orphanRemoval
est une proposition pertinente, et cela fonctionne bien. Je préférerais que la cascade fonctionne seule, mais votre réponse est toujours dans la portée.
Pas de problème, je suis content d'avoir pu aider :)
Afin de réaliser ce que vous avez demandé, j'ai peaufiné vos tableaux comme suit:
@Entity @Table(name = "a") public class A { @Id @Column(name = "b_id") @GeneratedValue(generator = "gen") @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name="property", value="b")) private Integer bId; @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true) @PrimaryKeyJoinColumn private B b; }
CASCADE DELETE
n'a pas été ajouté dans votre DDL. p>
Cela activera la suppression en cascade. Pour supprimer l'enregistrement b
lors de la suppression de a
, j'ai effectué les modifications suivantes dans la classe A
:
CREATE TABLE b ( dbid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY ); CREATE TABLE a ( b_id int(11) NOT NULL PRIMARY KEY REFERENCES b(dbid) ON DELETE CASCADE );
Cela a bien fonctionné. J'espère que cela aide.
Trouvez ici le lien vers la solution fonctionnelle.
p >
La suppression en cascade JPA n'a absolument aucune obligation de définir une suppression en cascade au niveau de la base de données.
Vous avez résolu le problème par des moyens complètement différents - en supprimant B au niveau de la base de données et non via Entity Manager (via cascade). De telles opérations peuvent avoir des effets secondaires: données incohérentes en session, pas de cascades en aval (déclenchées par JPA) de B vers, disons, C, données incohérentes dans le cache de 2e niveau, pas d'exécution des écouteurs d'entité pré-suppression définis pour B, etc.
D'accord avec @AlanHay
@AlanHay, acceptez que dans une telle situation, cela pourrait poser problème si le cache de deuxième niveau est utilisé. L'utilisation du cache de deuxième niveau est toujours discutable. Il faut être prudent lors de l'utilisation du cache de deuxième niveau. Cette solution n'a pas été fournie en ce qui concerne la mise en cache. Ni l'un ni l'autre n'est inclus dans la question. Oui, l'utilisation de la cascade peut être évitée ici. L'élimination des orphelins fait la magie ici. À votre santé!
En ce qui concerne uniquement le côté MySQL de votre implémentation , les enregistrements de la table B n'ont aucune "connaissance" des enregistrements de la table A. Dans la base de données, la relation est unidirectionnelle
La fonctionnalité de cascade native existe pour empêcher les erreurs de clé étrangère, en indiquant à la base de données ce qu'il faut faire lorsque la suppression d'un enregistrement ne laisserait une clé étrangère pointant nulle part. La suppression d'un enregistrement de table A ne provoquerait pas d'erreur de clé étrangère dans les enregistrements de table B, donc aucune fonctionnalité de cascade native ne serait déclenchée
Pour réitérer; Vous ne pouvez pas conserver le même schéma et la suppression en cascade de a
à b
, car vous ne disposez pas réellement de la suppression en cascade de a < / code> à
b
Vous avez également mentionné dans les commentaires que certains enregistrements de la table B peuvent exister sans enregistrements de la table A qui ne figurent pas dans la question d'origine
Pour obtenir la suppression automatique des enregistrements de la table B que vous décrivez, vous avez quelques options en ce qui concerne la base de données:
Il peut y avoir quelque chose dans JPA qui résout votre dilemme hors de la boîte, mais sous le capot, il fera l'une des choses ci-dessus (pas l'option 1 et probablement l'option 4)
Je ne suis pas tout à fait sûr que nous nous comprenions: je veux la cascade JPA et non celle de MySQL. Ce que vous dites est correct mais ma question portait sur l'option 4 utilisant la cascade JPA.
@PierreMardon C'est assez juste, mais je suppose que toute confusion que vous rencontrez au niveau JPA est due au fait que votre cascade n'a pas vraiment de sens au niveau DB sous-jacent. Je soupçonne que votre violation se produit parce que votre cascade JPA essaie de supprimer en cascade le B associé avant de supprimer le A ciblé (car c'est ainsi que les cascades fonctionnent normalement) .. il ne peut pas supprimer le B associé en premier car il est référencé par le A ciblé . Ce dont vous avez besoin n'est pas vraiment une suppression en cascade, car vous voulez d'abord supprimer le A ciblé puis le B. associé.
Oui, j'ai clairement compris que la conception est mauvaise, mais je dois y faire face;) Quoi qu'il en soit, il y a beaucoup de solutions de contournement (il suffit de faire la cascade à la main dans le code métier pour démarrer), ma question portait sur l'amélioration de ma compréhension de JPA; )
Pour vous aider à diagnostiquer le problème, veuillez fournir
SHOW CREATE TABLE
pour chacun des deux tableaux.Vous avez raison @RickJames, c'est plus clair, c'est fait.
Merci. Maintenant, veuillez justifier la nécessité d'un mappage 1: 1 entre une paire de tables. C'est presque toujours une "mauvaise conception de schéma" au début du développement. C'est généralement un kludge pour l'opportunité ou la performance lorsqu'il est fait plus tard dans le développement.
@RickJames Je ne veux pas expliquer le contexte complet de cette conception (pas du tout au début du développement), de toute façon c'est une des hypothèses de la question. Compte tenu de cela, puis-je atteindre la conception JPA que je cible?
Je préfère faire des suppressions en cascade (etc.) dans mon propre code. De cette façon, je n'ai pas à me demander si les FK "feront la bonne chose" ou "ne pourront pas faire cette cascade complexe".