Issue
I'm currently using Hibernate 6 and H2. I want to safely increment count
field of Entity
class but using more then 1 thread per time just to make sure that transaction is actually locking my entity. But when I ran this code, result count
column in H2 wasn't 10, but instead some random number under 10. What am I missing about pessimistic locking?
for (int a = 0; a < 5; a++) {
executorService.execute(() -> {
Session innerSession = sessionFactory.openSession();
Transaction innerTransaction = innerSession.beginTransaction();
Entity entity = innerSession.get(Entity.class, id, LockMode.PESSIMISTIC_WRITE);
entity.setCount(entity.getCount() + 1);
innerSession.flush();
innerTransaction.commit();
innerSession.close();
});
executorService.execute(() -> {
Session innerSession = sessionFactory.openSession();
Transaction innerTransaction = innerSession.beginTransaction();
Entity entity = innerSession.get(Entity.class, id, LockMode.PESSIMISTIC_WRITE);
entity.setCount(entity.getCount() + 1);
innerSession.flush();
innerTransaction.commit();
innerSession.close();
});
}
Entire method:
Long id;
SessionFactory sessionFactory;
Session session;
Transaction transaction;
ExecutorService executorService = Executors.newFixedThreadPool(4);
Properties properties = new Properties();
Configuration configuration = new Configuration();
properties.put(AvailableSettings.URL, "jdbc:h2:tcp://localhost/~/test");
properties.put(AvailableSettings.USER, "root");
properties.put(AvailableSettings.PASS, "root");
properties.put(AvailableSettings.DIALECT, H2Dialect.class.getName());
properties.put(AvailableSettings.SHOW_SQL, true);
properties.put(AvailableSettings.HBM2DDL_AUTO, Action.CREATE.getExternalHbm2ddlName());
// classes are provided by another library
entityClasses.forEach(configuration::addAnnotatedClass);
sessionFactory = configuration.buildSessionFactory(new StandardServiceRegistryBuilder().applySettings(properties).build());
session = sessionFactory.openSession();
transaction = session.beginTransaction();
// initial value of count field is 0
id = (Long) session.save(new Entity());
transaction.commit();
for (int a = 0; a < 5; a++) {
executorService.execute(() -> {
Session innerSession = sessionFactory.openSession();
Transaction innerTransaction = innerSession.beginTransaction();
Entity entity = innerSession.get(Entity.class, id, LockMode.PESSIMISTIC_WRITE);
entity.setCount(entity.getCount() + 1);
innerSession.flush();
innerTransaction.commit();
innerSession.close();
});
executorService.execute(() -> {
Session innerSession = sessionFactory.openSession();
Transaction innerTransaction = innerSession.beginTransaction();
Entity entity = innerSession.get(Entity.class, id, LockMode.PESSIMISTIC_WRITE);
entity.setCount(entity.getCount() + 1);
innerSession.flush();
innerTransaction.commit();
innerSession.close();
});
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
session.clear(); // prevent reading from cache
System.out.println(session.get(Entity.class, id).getCount()); // printed result doesn't match 10, same for reading from H2 browser interface
session.close();
Solution
Answer was simple, I need just to upgrade version of hibernate to 6.0.0.Alpha9. Higher versions requires 11 java to compile (I'm using 8). Seems like it was a bug in 6.0.0.Alpha6, which I used previously. There was no problem with H2 1.4.200. From hibernate sql logs I understood that the main problem in 6.0.0.Alpha6 was incorrect select
query for transaction with pessimistic lock, it was just regular select
, but in 6.0.0.Alpha9 already used select for update
, which prevents other transactions from reading this row.
Answered By - zexed640
Answer Checked By - Marilyn (JavaFixing Volunteer)