Hibernate - clearing a collection with all-delete-orphan and then adding to it causes ConstraintViolationException

后端 未结 4 2058
独厮守ぢ
独厮守ぢ 2021-02-19 14:19

I have these entities

class Foo{
    Set bars;
}

class Bar{
    Foo parent;
    String localIdentifier;
}

With this mapping (sorry,

相关标签:
4条回答
  • 2021-02-19 14:28

    If you are using oracle, you could also use deferrable constraints to postpone the checking of the constraints until the transaction is committed. Not sure if/how this is supported by other databases.

    0 讨论(0)
  • 2021-02-19 14:28

    I guess there is no alternative to flushing

    From here:

    Hibernate is violating a unique constraint!

    Hibernate isn't quite as clever with unique constraints as it is with foreign keys. Sometimes you might need to give a little hint.

    A unique constraint violation could occur if two objects are both being updated, one is "releasing" a value and the other is "obtaining" the same value. A workaround is to flush() the session manually after updating the first object and before updating the second.

    (This kind of problem occurs rarely in practice.)

    0 讨论(0)
  • 2021-02-19 14:49

    I had a slightly similar issue, where I had a unique index on the child table on certain condition. So my issue was when I remove a record that satisfied this condition, then add another record that will restore that condition back, I got the UNIQUE INDEX issue.

    After going through many trials and suggestions. I didn't like the idea of flushing after deleting and before the adding, I assume it will work though, I went to a constraint solution and dropped the unique index (as I didn't really needed the columns to be indexed) which did work:

    (On postgres database)

    ALTER TABLE bar ADD CONSTRAINT bar_uc
            EXCLUDE (parent_id WITH =) WHERE (condition = true) INITIALLY DEFERRED;
    

    INITIALLY DEFERRED is the key here.

    Sample code that I was using:

    //Transaction 1
    Foo parent = new Foo('some-id');
    boolean condition = true;
    Bar c = new Bar('some-id', parent, condition);
    parent.getChildren().add(c);
    fooService.save(parent);    //children list are cascaded
    

    ... later on

    //Transaction 2
    boolean condition = true;
    Foo parent = fooSerice.findById('some-id');    //children list is eagerly fetched
    Bar c1 = parent.getChildren().stream().filter(c -> c.getId().equals("some-id")).findFirst().get();
    Bar c2 = new Bar('some-id1', parent, condition);
    parent.getChildren().remove(c1);
    parent.getChildren().add(c2);
    
    0 讨论(0)
  • 2021-02-19 14:49

    If you want to avoid flushing the session here, try to replace the whole list (new List<Bar>() instead of Clear()). Hibernate should actually remove all the items in one shot before adding new. Just a try, not sure if it works.

    0 讨论(0)
提交回复
热议问题