Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate - @ElementCollection - Strange delete/insert behavior

@Entity public class Person {      @ElementCollection     @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID"))     private List<Location> locations;      [...]  }  @Embeddable public class Location {      [...]  } 

Given the following class structure, when I try to add a new location to the list of Person's Locations, it always results in the following SQL queries:

DELETE FROM PERSON_LOCATIONS WHERE PERSON_ID = :idOfPerson 

And

A lotsa' inserts into the PERSON_LOCATIONS table 

Hibernate (3.5.x / JPA 2) deletes all associated records for the given Person and re-inserts all previous records, plus the new one.

I had the idea that the equals/hashcode method on Location would solve the problem, but it didn't change anything.

Any hints are appreciated!

like image 503
nihilist84 Avatar asked Sep 18 '10 18:09

nihilist84


2 Answers

In addition to Pascal's answer, you have to also set at least one column as NOT NULL:

@Embeddable public class Location {      @Column(name = "path", nullable = false)     private String path;      @Column(name = "parent", nullable = false)     private String parent;      public Location() {     }      public Location(String path, String parent) {         this.path = path;         this.parent= parent;     }      public String getPath() {         return path;     }      public String getParent() {         return parent;     } } 

This requirement is documented in AbstractPersistentCollection:

Workaround for situations like HHH-7072. If the collection element is a component that consists entirely of nullable properties, we currently have to forcefully recreate the entire collection. See the use of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info. In order to delete row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than the current "WHERE COL = ?" (fails for null for most DBs). Note that the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing recreation isn't ideal, but not really any other option in ORM 4.

like image 39
Vlad Mihalcea Avatar answered Sep 20 '22 23:09

Vlad Mihalcea


The problem is somehow explained in the page about ElementCollection of the JPA wikibook:

Primary keys in CollectionTable

The JPA 2.0 specification does not provide a way to define the Id in the Embeddable. However, to delete or update a element of the ElementCollection mapping, some unique key is normally required. Otherwise, on every update the JPA provider would need to delete everything from the CollectionTable for the Entity, and then insert the values back. So, the JPA provider will most likely assume that the combination of all of the fields in the Embeddable are unique, in combination with the foreign key (JoinColunm(s)). This however could be inefficient, or just not feasible if the Embeddable is big, or complex.

And this is exactly (the part in bold) what happens here (Hibernate doesn't generate a primary key for the collection table and has no way to detect what element of the collection changed and will delete the old content from the table to insert the new content).

However, if you define an @OrderColumn (to specify a column used to maintain the persistent order of a list - which would make sense since you're using a List), Hibernate will create a primary key (made of the order column and the join column) and will be able to update the collection table without deleting the whole content.

Something like this (if you want to use the default column name):

@Entity public class Person {     ...     @ElementCollection     @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID"))     @OrderColumn     private List<Location> locations;     ... } 

References

  • JPA 2.0 Specification
    • Section 11.1.12 "ElementCollection Annotation"
    • Section 11.1.39 "OrderColumn Annotation"
  • JPA Wikibook
    • Java Persistence/ElementCollection
like image 53
Pascal Thivent Avatar answered Sep 22 '22 23:09

Pascal Thivent