-1

I have these two classes:

@Entity
public class Tag {

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

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent")
    private List<TagValue> values;

    private String name;
}

@Entity
public class TagValue {

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

    private String name;

    @ManyToOne
    private Tag parent;
}

These two are in a parent child relationship and a child knows it's parent. My problem starts when I try to delete a parent and a child is still referencing it. Eclipse Link tells me that I'm violating a foreign key constraint, because a child (TagValue) is still referencing it. How do I have to change my current approach to be able to delete the parent, when the child still has a reference to it?

And finally, how can I tell JPA to delete all childs that are referencing the parent when I delete the parent? It has to work like this. I know that it would be easier with the parent knowing it's children using a list, but that's not what I'm after. I played around with cascading and whatnot, but I just don't seem to get it.

EDIT:

So I was told, that it should already work with the code I have. Since it doesn't, here is the use case that fails and a stacktrace of the error.

TagValue value = new TagValue();
value.setName("Testvalue");

Tag tag = new Tag();
tag.setName("Testtag");
value.setParent(tag);
Database.addTag(tag);
Database.addTagValue(value);
Database.removeTag(tag);

The Database class methods are defined as follows:

public static void addTag(final Tag tag) throws Exception {
    manager.getTransaction().begin();
    manager.persist(tag);
    manager.getTransaction().commit();
}

public static void addTagValue(final TagValue value) throws Exception {
    manager.getTransaction().begin();
    manager.persist(value);
    manager.getTransaction().commit();
}

public static void removeTag(final Tag tag) throws Exception {
    manager.getTransaction().begin();
    manager.remove(tag);
    manager.getTransaction().commit();
}

The stacktrace:

Note: Auf Schlüssel (id)=(1) wird noch aus Tabelle „tagvalue“ means that the key (id)=(1) is still referenced in the table tagvalues.

[EL Warning]: 2015-02-24 11:12:43.785--UnitOfWork(390138887)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: FEHLER: Aktualisieren oder Löschen in Tabelle „tag“ verletzt Fremdschlüssel-Constraint „fk_tagvalue_parent_id“ von Tabelle „tagvalue“
  Detail: Auf Schlüssel (id)=(1) wird noch aus Tabelle „tagvalue“ verwiesen.
Error Code: 0
Call: DELETE FROM TAG WHERE (ID = ?)
    bind => [1 parameter bound]
Query: DeleteObjectQuery(Tag(id=1, values=[], name=Testtag))
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: FEHLER: Aktualisieren oder Löschen in Tabelle „tag“ verletzt Fremdschlüssel-Constraint „fk_tagvalue_parent_id“ von Tabelle „tagvalue“
  Detail: Auf Schlüssel (id)=(1) wird noch aus Tabelle „tagvalue“ verwiesen.
Error Code: 0
Call: DELETE FROM TAG WHERE (ID = ?)
    bind => [1 parameter bound]
Query: DeleteObjectQuery(Tag(id=1, values=[], name=Testtag))
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
    at de.yabue.baka.YabueCentral.Persistence.Database.removeTag(Database.java:256)
    at de.yabue.baka.YabueCentral.Persistence.DatabaseTest.testPersistence(DatabaseTest.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:74)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:673)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:846)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1170)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.runWorkers(TestRunner.java:1147)
    at org.testng.TestRunner.privateRun(TestRunner.java:749)
    at org.testng.TestRunner.run(TestRunner.java:600)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:317)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:312)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:274)
    at org.testng.SuiteRunner.run(SuiteRunner.java:223)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1039)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:964)
    at org.testng.TestNG.run(TestNG.java:900)
    at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:110)
    at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:205)
    at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:174)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:125)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: FEHLER: Aktualisieren oder Löschen in Tabelle „tag“ verletzt Fremdschlüssel-Constraint „fk_tagvalue_parent_id“ von Tabelle „tagvalue“
  Detail: Auf Schlüssel (id)=(1) wird noch aus Tabelle „tagvalue“ verwiesen.
Error Code: 0
Call: DELETE FROM TAG WHERE (ID = ?)
    bind => [1 parameter bound]
Query: DeleteObjectQuery(Tag(id=1, values=[], name=Testtag))
    at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1611)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:898)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:962)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:631)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
    at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2002)
    at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:298)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
    at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.deleteObject(DatasourceCallQueryMechanism.java:210)
    at org.eclipse.persistence.internal.queries.StatementQueryMechanism.deleteObject(StatementQueryMechanism.java:104)
    at org.eclipse.persistence.queries.DeleteObjectQuery.executeDatabaseQuery(DeleteObjectQuery.java:218)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
    at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
    at org.eclipse.persistence.queries.DeleteObjectQuery.executeInUnitOfWorkObjectLevelModifyQuery(DeleteObjectQuery.java:119)
    at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1786)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1737)
    at org.eclipse.persistence.internal.sessions.CommitManager.deleteAllObjects(CommitManager.java:336)
    at org.eclipse.persistence.internal.sessions.CommitManager.deleteAllObjects(CommitManager.java:285)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1444)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:277)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169)
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:132)
    ... 33 more
Caused by: org.postgresql.util.PSQLException: FEHLER: Aktualisieren oder Löschen in Tabelle „tag“ verletzt Fremdschlüssel-Constraint „fk_tagvalue_parent_id“ von Tabelle „tagvalue“
  Detail: Auf Schlüssel (id)=(1) wird noch aus Tabelle „tagvalue“ verwiesen.
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1836)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:512)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:334)
    at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
    ... 59 more

I also tried to get the tag I want to delete from the database first before I delete it, but the returned instance has no items in the values list. So my last guess is, that I have to fill the list values myself?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ya Bü
  • 21
  • 5
  • You're already doing it with cascade = ALL. Removing the parent should delete the children automatically. If it doesn't, show your code and the stack trace of the exception. – JB Nizet Feb 23 '15 at 20:27
  • as commented above it should already work (if you call EM.remove not doing a batched remove). You can add Tag.values.clear() before the remove. – Zielu Feb 23 '15 at 23:20
  • @JBNizet I added the information you requested. I'm sure it's a very trivial error, but i'm very new to JPA and are still trying to find my way around. – Ya Bü Feb 24 '15 at 10:36
  • You set the parent of the TagValue, but failed to add the TagValue to the values list of the Tag. – JB Nizet Feb 24 '15 at 10:52
  • @JBNizet Yes, you are right. I posted an answer that describes how i solved my problem. Thank you for your time! – Ya Bü Feb 24 '15 at 11:01

1 Answers1

1

Like my last thought suggested, i had to fill the list of the parent myself. So now there are no more errors.

I changed this method:

public static void addTagValue(final TagValue value) throws Exception {
    manager.getTransaction().begin();
    manager.persist(value);
    manager.getTransaction().commit();
}

to:

public static void addTagValue(final TagValue value) throws Exception {
    manager.getTransaction().begin();
    value.getParent().getValues().add(value);
    manager.persist(value);
    manager.getTransaction().commit();
}

And here is why i didn't look into this before: First, i somehow thought, that the mappedBy would automatically do the work. Also, i use lombok to generate my setters and getters and did so over the @Data annotation. This annotation generated all setters and getters but also toString() and hashCode() methods. When i added an item to the values list, the hashCode() method produced a Stackoverflow, which i had to solve first.

Ya Bü
  • 21
  • 5