简体   繁体   中英

Constraint violation in Hibernate unidirectional OneToMany mapping with JoinTable and OrderColumn when removing elements

I have a problem when removing elements from a list mapped as described above. Here is the mapping:

@Entity
@Table( name = "foo")
class Foo {

    private List bars;

    @OneToMany
    @OrderColumn( name = "order_index" )
    @JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns =  @JoinColumn( name = "bar_id" ) )
    @Fetch( FetchMode.SUBSELECT )
    public List getBars() {
        return bars;
    }
}

Inserting Bar-instances and saving the Foo works fine, but when I remove an element from the list and save again, the unique constraint on bar_id in the mapping table is violated. The following SQL-statements are issued by hibernate, and these look quite odd:

LOG:  execute : delete from foo_bar_map where foo_id=$1 and order_index=$2
DETAIL:  parameters: $1 = '4', $2 = '6'
LOG:  execute S_5: update foo_bar_map set bar_id=$1 where foo_id=$2 and order_index=$3
DETAIL:  parameters: $1 = '88', $2 = '4', $3 = '0'
ERROR:  duplicate key value violates unique constraint "foo_bar_map_bar_id_key"

The error perfectly makes sense, given the statements generated by Hibernate (there are five items in the list, I remove the first one and Hibernate deletes the mapping row with the LAST index and the tries to updates the remaining ones, starting with the first).

What is wrong with the mapping above?

Your mapping is totally valid and works with EclipseLink as JPA 2.0 implementation (without the Fetch annotation of course), but indeed fails with Hibernate.

Here is the DDL with Hibernate:

create table foo_bar_map (foo_id bigint not null, bar_id bigint not null, order_index integer not null, primary key (foo_id, order_index), unique (bar_id))
alter table foo_bar_map add constraint FK14F1CB7FA042E82 foreign key (bar_id) references Bar4022509
alter table foo_bar_map add constraint FK14F1CB7B6DBCCDC foreign key (foo_id) references Foo4022509

So let's say Foo#1 holds a list with Bar#1 , Bar#2 , Bar#3 , the join table contains:

foo_id | bar_id | order_index
     1 |      1 |           1
     1 |      2 |           2
     1 |      3 |           3

When removing, say the first item from the list, Hibernate first delete the last row (WTF?) from the join table:

foo_id | bar_id | order_index
     1 |      1 |           1
     1 |      2 |           2

And then tries to update the bar_id column in the join table instead of the order_index (WTF!?) to reflect the "new" ordering of the items in the list. First (schematically):

foo_id | bar_id | order_index
     1 |      2 |           1
     1 |      2 |           2

where the next step would result in:

foo_id | bar_id | order_index
     1 |      2 |           1
     1 |      3 |           2

Obviously, this approach doesn't sound right and doesn't work because of the unique constraint on bar_id . More generally, why the hell does Hibernate mess with the bar_id instead of updating the order_index column?

I consider this to be an Hibernate bug (reported as HHH-5694 , see HHH-1268 now).

Usually when joining through a join table the relationship is ManyToMany not OneToMany. Try this

@ManyToMany
@OrderColumn( name = "order_index" )
@JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns =  @JoinColumn( name = "bar_id" ) )
@Fetch( FetchMode.SUBSELECT )
public List getBars() {
    return bars;
}

No I don't think it's a hibernate bug, and as you will see if you do searches this hibernate bug quoted by Pascal Thivent is a bug known since 2006 and has never been solved.

Why ?

Cause I think the problem is just in the constraints on the table and not in hibernate.

I don't understand why there is a unique constraint on bar_id

Using an order index means your collection is a List (and not a Set !). And a List is a collection where you can specify index to elements you add (it corresponds to OrderColumn).
The difference between a List and a Set is that you and can the same data twice (or more), but the same data will be at different indexes. Then you can have the same bar_id a different indexes, you don't have to specify a unique constraint on bar_id. And the primary key can't be (foo_id, order_index) cause the pattern List authorize the same data at different indexes. Maybe your PK should be (foo_id, bar_id, order_index) ?

I think the problem is in this way :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM