Recently, when I use JPA(1.3.0.final) and Hibernate(4.2.6.final) on Play 2.2.1 framework, I got errors like:
java.lang.IllegalStateException: Error occurred while storing entity [xxx] An entity copy [xxx] was already assigned to a different entity [yyy]
And none of the existing solutions on the Internet solves my prbolem.
Now, let me explain a bit about the story.
I have three entities, Owner, Box, and Item.
Assumptions:
- An Item or a Box must have an Owner;
- A Box may contain zero or more Items, while an specific Item may not belong to any Box.
- When an Item is put into a Box, they may or may not have the same Owner.
Entity Class:
/* for simplicity, others fields, getter and setter are ommitted*/
The simplified Owner class definition
@Entity
@Table(name = "OWNER")
public class Owner implements Serializable{
@Id
@Column(name="ID")
public String uuid = UUID.randomUUID().toString();
@Override
public int hashCode() {
int result = 1;
char[] charArray = this.uuid.toCharArray();
int n = charArray.length;
int powbase = (int) Math.pow(31, n - 1);
for(int i = 0; i < n; i++){
if ( i != 0){
powbase = powbase / 31;
}
result += (int)charArray[i] * powbase;
}
return result;
}
@Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(!(obj instanceof Item)){
return false;
}
Item other = (Item)obj;
//Box other = (Box)obj;
//Owner other = (Owner)obj;
if(!other.uuid.equals(this.uuid)){
return false;
}
return true;
}
}
The implementation of hashCode() and equals() of Item and Box are similar to Owner. For sake of argument, let's omit these.
The simplified Item class definition
@Entity
@Table(name = "ITEM")
public class Item implements Serializable{
@Id
@Column(name="ID")
public String uuid = UUID.randomUUID().toString();
/* single-directional mapping*/
@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
@JoinColumn(name = "O_ID")
public Owner owner;
/* bi-directional mapping*/
@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH })
@JoinColumn(name = "B_ID")
public Box box;
}
The simplified Box class definition.
@Entity
@Table(name = "BOX")
public class Box implements Serializable {
@Id
@Column(name="ID")
public String uuid = UUID.randomUUID().toString();
/* single-directional mapping*/
@ManyToOne(cascade = { CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
@JoinColumn(name = "O_ID")
public Owner owner;
/* bi-directional mapping*/
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REFRESH,
CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER, mappedBy = "box")
public Set<Item> items = new HashSet<Item>();
}
The business logic is shown as follows:
public class Test {
public static void main(String[] args) {
Owner owner = new Owner();
JPA.em().persist(owner);
Box box = new Box();
box.owner = owner;
JPA.em().persist(box);
Item boxItem = new Item();
boxItem.owner = owner;
boxItem.box = box;
box.items.add(boxItem);
JPA.em().persist(boxItem);
Item nonBoxItem = new Item();
nonBoxItem.owner = owner;
JPA.em().persist(nonBoxItem);
/* in the future, put the 'nonBoxItem' into a box*/
Item itemToUpdate = JPA.em().find(Item.class, nonBoxItem.uuid);
itemToUpdate.box = box;
box.items.add(itemToUpdate);
JPA.em().merge(itemToUpdate); /* error occurred here*/
}
}
Does anyone have any idea to solve this problem?
Thanks.