0

I'm in the process of migrating a Hibernate 3 application to Hibernate 5.2. We have a couple of one-to-one unidirectional mappings with cascading (which we can't make bidirectional because the parent can't know anything about the other entities). The following (simplified example) worked fine in the latest Hibernate 3.x and latest Hibernate 4.x, but started running into ConstraintViolationExceptions in Hibernate 5.2:

public class Person {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    @Column(name="person_id", length=32)
    public String id;

    /* other fields, getters and setters */
}
public class EnhancedPerson {
    @Id
    @Column(name="person_id", length=32)
    @GeneratedValue(generator="foreign")
    @GenericGenerator(
        name="foreign",
        strategy = "foreign",
        parameters = { @Parameter(name="property", value="person") }
    )
    private String id;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @PrimaryKeyJoinColumn
    private Person person;

    /* other fields, getters and setters */
}

As far as I could dig, when deleting an EnhancedPerson, hibernate tries first to delete the Person (because of the cascade), and of course this runs into a foreign key violation (because the EnhancedPerson must reference a Person, so it needs to be deleted first).

I tried simplifying the mapping to use JPA annotations as follows:

public class EnhancedPerson {
    @Id
    private String id;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @MapsId
    @JoinColumn("person_id")
    private Person person;

    /* other fields, getters and setters */
}

But the result has been the same. As far as I could dig, the usual recommendation is not to cascade from child to parent (e.g. this answer), also hibernate's documentation says Only the parent side of an association makes sense to cascade its entity state transitions to children.

I've also found this conversation that seems related, but the complaint is from Hibernate 5.3 to 5.4, and in my case it stopped working when going from 4.x to to 5.2. Also, I think the terminology is swapped there (what's called Child is the parent/owner of the relation, and what's called Parent is the child/has the foreign key) unless I'm the one with the confusion.

Any pointers how I could fix this, or if this works fine in more recent Hibernate versions? It might not normally make sense to cascade from child to parent, but in our case if the child is deleted, it makes no sense to keep the parent around (also, if the parent reference is modified through the child, those changes should also be persisted when persisting the child). Also, I wonder why this used to be supported and isn't anymore (I'm guessing a bug, but it could have been a purposeful breaking change).

contivero
  • 59
  • 2
  • 6
  • This shouldn't be an issue as it is a regular 1:1 mapping. Parent doesn't need to know it is referenced by anything else for this to work, (hibernate and other providers should detect the class dependency and so delete Parent last) so something else is going on. Can you turn off constraint checking and see the order of all the statements in the transaction? This might point you at something else - an unshown dependency maybe from Parent to something else that is forcing your provider to think Parent needs to go first. – Chris Oct 17 '22 at 16:05
  • Hibernate 5.2 is very old at this point and unsupported. Try updating to Hibernate 5.6 at least. If you have the same issue on that version, please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue. – Christian Beikov Oct 17 '22 at 18:12
  • @Chris I realized this first because our test suite is failing. The test is a transactional test, where among other things we delete the child, and we then proceed to call a `loadAll` to see that there is nothing in the DB remaining. When `loadAll` is called, Hibernate flushes the deletion, and is issuing the deletion of the parent before that of the child (I enabled debug logging to see the SQL statements Hibernate was generating). But I'll try digging a bit deeper into the order and see if I can find something, thanks! – contivero Oct 17 '22 at 19:41
  • @ChristianBeikov though I completely agree with you, the jump from 3 to 5 hasn't been small (I haven't finished it yet), and going past 5.2 implies having to migrate a lot of legacy queries using positional parameters. So I'm hoping I can fix this without needing to do that (right now, but I hope to do that too eventually). In any case, I can likely try with 5.6 and see if the tests work fine there, so I'll do that, thanks for the idea! – contivero Oct 17 '22 at 19:46
  • @ChristianBeikov was able to try with 5.6.12 and it works fine there, so it looks like it's a bug with 5.2.18. Thanks for the help! You might want to post it as an answer so that I can accept it. – contivero Oct 17 '22 at 21:34

1 Answers1

1

Reposting this as answer - Hibernate 5.2 is very old and lots of bugs have been fixed in the meantime in newer versions. As you found out by trying, the problem is gone in Hibernate 5.6.12.Final, so the solution is to update your dependencies ;)

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58