I have an exception when calling an update() method of the UserService class, in order to update a user's data saved in the db.
UserService class:
import java.util.List;
import db.hibernate.dao.UserDao;
import models.User;
public class UserService {
private static UserDao userDao;
public UserService() {
userDao = new UserDao();
}
public void persist(User entity) {
userDao.openCurrentSessionwithTransaction();
userDao.persist(entity);
userDao.closeCurrentSessionwithTransaction();
}
public void update(User entity) {
userDao.openCurrentSessionwithTransaction();
userDao.update(entity);
userDao.closeCurrentSessionwithTransaction();
}
/*otros metodos*/
The class is called from the PaneUser view controller when clicking the "Delete" button:
PaneUserController class:
public class PaneUserController {
private UserService userService = new UserService();
List<User> allUsers = userService.findAll();
public PaneUserController(PaneUser paneUser) {
//codigo
paneUser.addCrudListener(new CrudListener());
initUserView();
}
public void initUserView() {
theUserView.setFieldData(allUsers.get(0));
}
class CrudListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//crea un user con los datos de input
User user = theUserView.getFieldData();
switch (e.getActionCommand()) {
case "Search":
// opens the searchUI
break;
case "New...":
CardLayout cardLayout = (CardLayout) pUser.getLayout();
cardLayout.show(pUser, "Pane attributeSet");
break;
case "Update":
userService.update(user); // lanza el error en cuestion
break;
case "Delete":
userService.delete(user.getUserID());
break;
}
}
}
UserDao class:
import models.User;
public class UserDao implements UserDaoInterface<User, String> {
private Session currentSession;
private Transaction currentTransaction;
public UserDao() {
}
public Session openCurrentSession() {
currentSession = getSessionFactory().openSession();
return currentSession;
}
public Session openCurrentSessionwithTransaction() {
currentSession = getSessionFactory().openSession();
currentTransaction = currentSession.beginTransaction();
return currentSession;
}
public void closeCurrentSession() {
currentSession.close();
}
public void closeCurrentSessionwithTransaction() {
currentTransaction.commit();
currentSession.close();
}
private static SessionFactory getSessionFactory() {
Configuration configuration = new Configuration()
.addAnnotatedClass(models.User.class)
.addAnnotatedClass(models.Client.class).configure();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
SessionFactory sessionFactory = configuration.buildSessionFactory(builder.build());
return sessionFactory;
}
public Session getCurrentSession() {
return currentSession;
}
public void setCurrentSession(Session currentSession) {
this.currentSession = currentSession;
}
public Transaction getCurrentTransaction() {
return currentTransaction;
}
public void setCurrentTransaction(Transaction currentTransaction) {
this.currentTransaction = currentTransaction;
}
public void persist(User entity) {
getCurrentSession().save(entity);
}
public void update(User entity) {
getCurrentSession().update(entity);
}
public User findById(String id) {
User book = (User) getCurrentSession().get(User.class, id);
return book;
}
public void delete(User entity) {
getCurrentSession().delete(entity);
}
@SuppressWarnings("unchecked")
public List<User> findAll() {
List<User> users = (List<User>) getCurrentSession().createQuery("from User order by userid").list();
return users;
}
public void deleteAll() {
List<User> entityList = findAll();
for (User entity : entityList) {
delete(entity);
}
}
}
The StackTrace:
ERROR: HHH000346: Error during managed flush [Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [models.User#1]]
Exception in thread "AWT-EventQueue-0" javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [models.User#1]
at org.hibernate.internal.ExceptionConverterImpl.wrapStaleStateException(ExceptionConverterImpl.java:226)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:93)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1460)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98)
at db.hibernate.dao.UserDao.closeCurrentSessionwithTransaction(UserDao.java:38)
at db.hibernate.service.UserService.update(UserService.java:24)
at controllers.users.PaneUserController$CrudListener.actionPerformed(PaneUserController.java:63)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [models.User#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2522)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3355)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3229)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3630)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:146)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
... 47 more
I'm sorry if I've written a lot of code, but I see it as necessary, what could be the cause of this error?
The error that comes out is because there are two simultaneous transactions modifying the same database row.
The first one that commits is the one that ends up updating the table, and the second one is rejected when committing since it would be stepping on the changes of the previous transaction.
For clarity, the two transactions start with the same data. It would be different if one complete transaction occurs first, and then the other, starting with the data of the previous transaction already committed.
See how Hybernate's locking strategies work