Home > other >  Does Hibernate/Spring write immediately to database if no @Transactional is used?
Does Hibernate/Spring write immediately to database if no @Transactional is used?

Time:04-03

I summarize the case below, but what I am facing seems to be a "lost update" issue even though I am using locks that do not allow more than one thread at a time to be able to write to the DB.

Locks, i.e., reentrant locks, synchronized, semaphores, etc, all work well in preventing more than one thread to gain access to the shared area.. no issue here. The following is the setup in a nutshell:

I get two Java threads as a result of two users clicking on a button to increase a counter. Each user issues repeated updates to the counter, which is a field in a Mysql table. Basically, If users click on the button slowly, there is no lost update. When they speed up the clicking on the UI of each user, I see the missed updates.

In order to prevent the two threads from stepping on each other's foot, each thread needs to get a lock to the shared code area. I would not use java locks if I had multiple servers. The whole application is on a single server.

What I am seeing is that even though a thread writes to the database and releases the lock, the next thread sees some older value for the field. I removed all @Transactional statements to rule out any Spring tranactions issues, but the lost update is still there.

So, if the repository (DAO) writes to Mysql, doesn't that guarantee that the value is committed and persisted? Remember, no @Transactional statements are used here. I also tried flush after save in the repository module.

Any ideas? What am I missing?

CodePudding user response:

@Transactional annotation is always used for data modifying methods. It is used not only for a transaction itself, but to open and close a persistent context (Hibernate session) too!

For example you can check an implementation of JpaRepository save() method

    @Transactional
    @Override
    public <S extends T> S save(S entity) {

        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }

An idea to use java locks is not very good. Why you have such behavior? I think it is not a transactional issue, probably you have implemented multithreading code incorrectly.

The best way is to use JPQL update to update a counter

update ButtonEntity set couner = counter   1 where id = :buttonId

Another option is to use a pessimistic lock.

CodePudding user response:

First of all , @Transactional must be enabled if you want to update a DB record.If you remove all @Transactional , you cannot make any updates. I guess you are now using Spring Data JPA repository which behind the scene @Transactional is enable on its save() method. So even you remove all the @Transactional from your codes , there still a @Transactional here such that you can update the record.

For solving the lost update issue in Hibernate way , you can either use optimistic locking or pessimistic locking.

For optimistic locking , you add a @Version property in your entity . If there is a lost update , it will throw OptimisticLockException . You simply catch it and retry again . Refer it for details.

For pessimistic locking, it works like the synchronized in Java to ensure only one thread can update an record at a time. But the lock is managed by DB rather than JVM , so it is distributed lock which solve your concern if your application is executed on multiple servers . You can refer to this for how to do it in Spring Data JPA or this for plain JPA.

  • Related