Hibernate - @ElementCollection - Strange delete/insert behavior
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 theId
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 theEntity
, and
then insert the values back. So, the
JPA provider will most likely assume
that the combination of all of the
fields in theEmbeddable
are unique,
in combination with the foreign key
(JoinColunm
(s)). This however could be
inefficient, or just not feasible if
theEmbeddable
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
@ElementCollection, @CollectionTable and Enum - Strange delete/insert behavior
@OrderColumn solved the problem
Hibernate - delete insert db commands about ElementCollection
Clearly the JPA sees that the objects are not equal are clears and re-inserts.
Please try overriding the equals()
and hashcode()
for the Embeddable
object. With default implementation, object is considered equal by reference alone, which would have not been in your case.
hibernate insert to a collection causes a delete then all the items in the collection to be inserted again
I have inserts acting the way I expect them to now. Thanks to Pascal and z5h, I have learned a lot. I believe I have the hashCode and equals implemented correctly. This never solved the problem for me though. Instead I have implemented an Intermediate Entity.
For what it is worth below are the mapping in my Employee, CohortGroup, and now CohortGroupMemeber classes.
Employee:
@OneToMany(mappedBy="member")
public List<CohortGroupMember> getMemberGroups(){
return memberGroups;
}
public void setMemberGroups(List<CohortGroupMember> grps){
memberGroups = grps;
}
CohortGroupMember
@ManyToOne
@JoinColumn(name="USERID")
public Employee getMember(){
return emp;
}
public void setMember(Employee e){
emp = e;
}
@ManyToOne
@JoinColumn(name="COHORT_GROUPID")
public CohortGroup getGroup(){
return group;
}
public void setGroup(CohortGroup cg){
group = cg;
}
CohortGroup
@OneToMany(mappedBy="group")
public List<CohortGroupMember> getMembers(){
return members;
}
public void setMembers(List<CohortGroupMember> emps){
members = emps;
}
The book I followed for this is Java Persistence with Hibernate chapter 7.2.3
Removing element from JPA element Collection
You need to identify the object you want to remove (obviously without id since there is none, but the combination of other fields should be unique), remove it from owner entity's collection and merge the entity.
Since there is no id, JPA will delete all elements from collection, and insert them again but without the removed one.
Branch toBoRemoved = ...; // find out which element needs to be removed
for (Iterator i = entity.getBranches().iterator(); i.hasNext();) {
Branch b = (Branch)i.next();
if (b.equals(toBeRemoved)) { // you'll need to implement this
i.remove();
}
}
entityManager.merge(entity);
JPA - Adding and Removing elements in List with ElementCollection
What I ended up doing was creating an entity of my own instead of using annotations to create the fields table, this is the Store entity:
@Entity
public class Store {
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
@OrderBy("index ASC")
private List<Item> itemsList;
@Column
private int index = 0;
public List<Item> getItemsList() {
return itemsList;
}
public void setItemsList(List<Item> itemsList) {
this.itemsList = itemsList;
}
public void addToItemList(String item) {
final Item newItem = new Item();
newItem.setStoreId(this.id);
newItem.setValue(item);
newItem.setIndex(index++);
this.itemsList.add(newItem);
}
}
this is the Item entity which the store will hold a list of:
@Entity
@IdClass(Ids.class)
public class Item {
@Id
private long storeId;
@Id
private int index;
@Column
private String value;
public long getStoreId() {
return storeId;
}
public void setStoreId(long storeId) {
this.storeId = storeId;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
class Ids implements Serializable { // each id will be composed of storeId + index
long storeId;
int index;
}
strange sql behavior when do deleting relation for many-to-many in JPA
I've not checked that this explanation is true, but it has good points.
Without any index column, a List is in fact a bag: no order, and duplicates allowed. So Hibernate considers it possible that you have the same role twice in the list of roles of a user.
So issuing delete from user_role where user_id = ? and role_id = ?
is not possible because it would potentially remove several roles instead of just the one you removed from the list.
Try adding an index column, or using a Set<Role>
instead of a List<Role>
.
Related Topics
How to Get a List of Ip Connected in Same Network (Subnet) Using Java
Bigdecimal Equals() Versus Compareto()
How to Format a String in an Email So Outlook Will Print the Line Breaks
Removing Invalid Xml Characters from a String in Java
How to Lower Java Heap When Not in Use
How to Use Printwriter and File Classes in Java
Entry Point for Java Applications: Main(), Init(), or Run()
How to Use Audio Sample Data from Java Sound
How to Set Dpi Information in an Image
Do Hibernate Table Classes Need to Be Serializable
Java Serialization with Non Serializable Parts
How to Copy a Java.Util.List into Another Java.Util.List
Read File from Resources Folder in Spring Boot
Hibernate Criteria: Joining Table Without a Mapped Association
What Is the Main-Stream Java Alternative to ASP.NET/Php
Why Do We Assign a Parent Reference to the Child Object in Java