Persisting a new but identical entity with JPA reporting duplicate entry
I have a JPA project connected to a MySQL database where my entity object is mapped to a table with a constraint on 2 columns. That is:
@Entity
@Table(name = "my_entity")
class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "myField1")
private String myField1;
@Basic(optional = false)
@Column(name = "myField2")
private int myField2;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "myEntity")
private Set<OtherEntity> otherEntitySet;
}
In the database, the my_entity table has a unique constraint on (myField1, myField2). The issue is that if I remove an existi开发者_Go百科ng entity with EntityManager.remove(entity) and then add a new one with EntityManager.persist(entity), the database throws an error about a duplicate row.
For example:
entityManager.getTransaction().begin();
MyEntity entity1 = new MyEntity();
entity1.setMyField1("Foo");
entity1.setMyField2(500);
entityManager.persist(entity1);
entityManager.getTransaction().commit();
entityManager.getTransaction().begin();
entityManager.remove(entity1);
MyEntity entity2 = new MyEntity();
entity2.setMyField1("Foo");
entity2.setMyField2(500);
entityManager.persist(entity2);
entityManager.getTransaction().commit();
This gives me a MySQLIntegrityConstraintViolationException complaining about this being a duplicate entry. I imagine it's because it's trying to add the new entry before removing the old one. Is there any way to maintain that order? Or, is there a way to use JPA to prevent this situation? It's not exactly a common use case, but I'm concerned about a user who tries to delete an entity to get rid of all the associated data and start over, and then recreates the easier fields, then finding that the data was never removed.
The hashCode and equals implementations are as follows:
public int hashCode() {
int hash = 0;
hash += (getMyField1().hashCode() + getMyField2());
return hash;
}
public boolean equals(Object object) {
if (!(object instanceof MyEntity)) {
return false;
}
MyEntity other = (MyEntity) other;
return (getMyField2() == other.getMyField2()) &&
(getMyField1().equals(other.getMyField1()));
}
I don't think there is any standard way to specify operational ordering in JPA, so there is no guarantee which order the statements will be executed. Ideally, the JPA implementation would be clever enough to detect this situation and perform the delete before the insert, but that is an area they frequently fail in. Alternatively, if your database supported deferred constraint checking (e.g., Oracle does but MySQL doesn't), the database would handle this by waiting until commit time to give the unique constraint violation exception.
So one solution is to just perform an extra commit after your call to remove(entity1). Another possibility would be, before you create entity2, to first check to see if it exists in the database, and if so, just use that one. Both these options can be somewhat cumbersome and not be suitable for all workflows. You might want to dig into the documentation for your current JPA implementation to see if they offer any extensions that might help.
I had the same problem involving a UC and the insertions/deletions occurring in the wrong order. I had to create a composite key for my entity that matched the columns of the UC. After that, the deletions and insertions occurred in the correct order. See this post, and the comment about adding an @EmbeddedId:
https://forum.hibernate.org/viewtopic.php?p=2382504
精彩评论