Home > other >  How does pessimistic locking work in Hibernate?
How does pessimistic locking work in Hibernate?

Time:08-05

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);

// 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();

CodePudding user response:

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.

  • Related