Cascade not cleaning Persistent Context


#1

Hi,

I’ve run into an issue where cascade removals (the Hibernate ones) do propagate into database, but the objects still remain in the Persistence Context, which causes an error.

Here I have prepared a sample project, which reproduces the issue: https://github.com/scadgek/SchemaRegistry. It contains a JUnit test with one method demostrating the problem, on each run the database is recreated based on persistence.xml.

To describe the use case in a few words, imagine you’re trying to store the metadata of an object database (or class hierarchy) into relational database. This way, you have UserDataTableEntity, which has many UserDataTableColumnEntities (for simple fields), some of which can be UserDataTableRelationEntities (for object fields), which relate to some UserDataTableEntity.

What I’m doing in the project is I save some test data first - two tables, one column belonging to the first table, this column is also a relation to the second table. Then I try to remove the tables one by one and expect that everything is cleared up based on entities’ annotations and database constraints. As a result, the first table is removed correctly - I can see there’s nothing left for it in the database, but apparently the Persistence Context is not cleaned up - I can see the Column and Relation entity are still there, and therefore the deletion of the second table fails with the following exception:

javax.persistence.RollbackException: Error while committing the transaction
...
Caused by: java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : edu.scadge.schemaregistry.datamodel.UserDataTableColumnEntity.userDataTable -> edu.scadge.schemaregistry.datamodel.UserDataTableEntity
...
Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : edu.scadge.schemaregistry.datamodel.UserDataTableColumnEntity.userDataTable -> edu.scadge.schemaregistry.datamodel.UserDataTableEntity
...

(you can see it in full if you run the test case)
Hope my explanations were at least a bit clear, though anyway you can pull the project above and see everything by yourself - I tried to minimize it as much as possible.
I guess as a solution I need to cleanup the context manually, but I always thought it’s JPA/Hibernate’s responsibility to keep it in sync. Probably I’m missing something, so will be really grateful for any advice.


#2

Why are you using both CascadeType.ALL and @OnDelete?

@OneToMany( mappedBy = "userDataTable", cascade = CascadeType.ALL, orphanRemoval = true )
  @OnDelete( action = OnDeleteAction.CASCADE )
  public Collection<UserDataTableColumnEntity> getUserDataTableColumns()
  {
    return userDataTableColumns;
  }

It’s not clear from your test case what’s wrong. Send us a Pull Request with a test case as explained in this article or use the standard Hibernate test case template as it’s much easier to integrate it afterward in case the issue is replicating.


#3

Hello Vlad,

Sorry, I just added a part of the stacktrace from the exception I receive when deleting the second table. You can easily see it if you pull and run the sample project I attached.

As to your question about cascade and @OnDelete - that’s an interesting one. In the documentation I found that @OnDelete annotation should be placed on the other part of the relation (which is @ManyToOne) and that’s what I did initially. But I found that the schema generation ignores it for some reason and does not generate ON DELETE CASCADE in the database schema. At the same time, if I put it on the @OneToMany side of the relation, the constraint is generated properly. So, I don’t know whether it’s an issue with the docs or with Hibernate, I just found that it’s the only way to make it work as expected.

By the way, this is not true for @OneToOne relation - as you see in the code, @OnDelete is placed properly, like the documentation requires, and does generate a proper constraint.


#4

A test case is worth 1000 words. Please send us a Pull Request with the test case as mentioned in the other post. Thanks.


#5

Sure, here it is: https://github.com/hibernate/hibernate-orm/pull/2520


#6

I’ll take a look on it tomorrow.


#7

Thank you very much, Vlad! Looking forward to your reply.


#8

Problem solved.


#9

Hi again, Vlad!
Thank you for your investigation, indeed I forgot to set the relations from the both sides and your patch made the test pass.
But later I found that I had a mistake in the test data creation, and after fixing it the test started to fail, finally reproducing the behavior I have in the original app. Here’s the “fixed” failing test: https://github.com/hibernate/hibernate-orm/pull/2527