Issue
I have a DAO class with the following implementation:
@Override
public void save(Employee emp) {
Session curSession = entityManager.unwrap(Session.class);
curSession.saveOrUpdate(emp);
}
and a service class with the below implementation:
@Override
@Transactional
public void save(Employee emp) {
employeeDAO.save(emp);
}
When I execute this code it throws me a StaleObjectStateException - org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
But if I set the id before calling the DAO class, it works fine -
@Override
@Transactional
public void save(Employee emp) {
emp.setId(0);
employeeDAO.save(emp);
}
Entity Class -
@Entity
@Table(name="employee")
public class Employee {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@Column(name="email")
private String email;
public Employee() {
}
public Employee(int id, String firstName, String lastName, String email) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
//getters and setters for the fields are present
Can anyone help me understand what exactly is happening here?
Solution
As I don't know the state of your session and DB when you get to stale object exception, I cannot be sure. However, here are some possible solutions to your problem based on my educated guesses :
Refer to this question for characteristics of saveOrUpdate() : Hibernate saveOrUpdate behavior
- if the object is already persistent in this session, do nothing
- if another object associated with the session has the same identifier, throw an exception
- if the object has no identifier property, save() it
- if the object's identifier has the value assigned to a newly instantiated object, save() it
- if the object is versioned by a "version" or "timestamp", and the version property value is the same value assigned to a newly instantiated object, save() it otherwise update() the object
Make sure you're not calling saveOrUpdate() twice on the same object anywhere, as this will cause a stale object exception.
Secondly, refer to this question : Hibernate saveOrUpdate fails when I execute it on empty table
This is by design.
Hibernate only decides based on the id if it should perform an update or an insert. If it would consider what's already there in the database, it would have to perform an additional query, but it doesn't do this.
Ids are normally managed by Hibernate, unless you map it as generator="assigned" (don't know how this looks using annotations). So you shouldn't just assign a value to it.
In this case, the Id is even given by the database. So if your application would generate Ids at the same time, it would result in duplicated Ids. (And most database do not allow inserting values to auto columns, so it would technically be impossible to store your id).
If you want to generate your own Ids, use generator="assigned", and store it using merge. This will do what you expect: it searches the record in the database, when it exists it will update it, when not it will insert it.
Because Hibernate doesn't do a Select query to perform an update or insert, make sure you are not manually creating records in your DB for tables that are supposed to be managed by Hibernate. Create actual instances of objects and save test objects to your DB instead.
Answered By - John Kim
Answer Checked By - Terry (JavaFixing Volunteer)