Transactions

Transactions in JPA

Transactions in JPA

Механизмы обработки транзакций при работе с JDBC

  • jdbc (the default for non-Jakarta Persistence applications)

  • jta (Jakarta Transactions/Java Transaction API)

Example

public class DepartmentDao {
    public create(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        Department economist = new Department(name);
        try {
            em.getTransaction().begin();
            em.persist(economist);
            // Other business logic stuff
            em.getTransaction().commit();
        } catch (RollbackException e) {
            em.getTransaction().rollback();
        }
        em.close();
    }
}

How setting Isolation level

<properties>
    ...
    <property name="jakarta.persistence.jdbc.isolation" value="2"/>
</properties>
  • 0 – транзакции не поддерживаются

  • 1 – изоляция уровня чтения неподтвержденного

  • 2 – изоляция уровня чтения подтвержденного

  • 4 – изоляция уровня повторяемого чтения

  • 8 – упорядоченная изоляция

Locking

Locking

  • Транзакции, как средство разграничения параллельной работы с данными, идут рядом с аналогичным средством разграничения, Locking (блокировками).

  • Блокировки, это механизм, позволяющий параллельную работу с одними и теми же данными в базе данных.

  • Когда более чем одна транзакция пытается получить доступ к одним и тем же данным в одно и то же время, в дело вступают блокировки, которые гарантируют, что только одна из этих транзакций изменит данные.

Locking strategies

  • Optimistic (for read-often-write-sometimes situations)

  • Pessimistic

Ex: without locking

public class CatDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        Cat cat = em.find(Cat.class, 1L);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

select catlockall0_.id as id1_0_0_, catlockall0_.name as name2_0_0_
from CatLockAll catlockall0_ where catlockall0_.id=?
update CatLockAll set name=? where id=? and name=?

Ex: OptimisticLockType.VERSION

@Data @AllArgsConstructor @NoArgsConstructor
@Entity
@OptimisticLocking(type = OptimisticLockType.VERSION)
public class CatLockVersion {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String owner;
    @Version
    private Integer version;
}

Ex: OptimisticLockType.VERSION

public class CatLockVersionDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockVersion cat = em.find(CatLockVersion.class, 1L);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

Select id, name, owner, version
from CatLockVersion  where id=1
update CatLockVersion
set name= New, owner= Tim, version=1 where id=1 and version=0

Ex: OptimisticLockType.ALL

@Data @AllArgsConstructor @NoArgsConstructor
@Entity
@DynamicUpdate
@OptimisticLocking(type = OptimisticLockType.ALL)
public class CatLockAll {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String owner;
}

Ex: OptimisticLockType.ALL

public class CatLockAllDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockAll cat = em.find(CatLockAll.class, 1L);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

select id, name, owner
from CatLockAll  where id=1
update CatLockAll
set name= New where id=1 and name=AllCat and owner=Tim

Ex: OptimisticLockType.DIRTY

@Data @AllArgsConstructor @NoArgsConstructor
@Entity
@DynamicUpdate
@SelectBeforeUpdate
@OptimisticLocking(type = OptimisticLockType.DIRTY)
public class CatLockDirty {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String owner;
}

Ex: OptimisticLockType.DIRTY

public class CatLockDirtyDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockDirty cat = em.find(CatLockDirty.class, 1L);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

select id, name, owner
from CatLockDirty where id=1
update CatLockDirty
set name= New where where id=1 and name=Dirty

Ex: optimistic

public class CatLockVersionDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockVersion cat = em.find(CatLockVersion.class, 1L, LockModeType.OPTIMISTIC);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

select id, name,  owner, version
from CatLockVersion where id=1
update CatLockVersion set name=New, owner=Tim, version=1 where id=1 and version=0
select version from CatLockVersion where id =1

Ex: optimistic with error

public class CatLockVersionDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockVersion cat = em.find(CatLockVersion.class, 1L, LockModeType.OPTIMISTIC);
        cat.setName(name);
        new Thread(()-> {
            EntityManager entityManager = HibernateUtil.getEntityManager();
            entityManager.getTransaction().begin();
            CatLockVersion updatedCat = entityManager.find(CatLockVersion.class, 1L);
            updatedCat.setName("Updated Cat");
            entityManager.getTransaction().commit();
        }).start();
        Thread.sleep(500);
        em.getTransaction().commit();
    }
}

Output

select id , name, owner, version from CatLockVersion where id=1
select id , name, owner, version from CatLockVersion where id=1
update CatLockVersion set name= Updated Cat, owner= Tim, version=1 where id=1 and version=0
update CatLockVersion set name= New, owner= Tim, version=1 where id=1 and version=0
HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1]

Ex: optimistic with increment

public class CatLockVersionDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        CatLockVersion cat = em.find(CatLockVersion.class, 1L, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
        cat.setName(name);
        em.getTransaction().commit();
        em.close();
    }
}

Output

select id, name,  owner, version
from CatLockVersion where id=1
update CatLockVersion set version=1 where id=1 and version=0

Ex: pessimistic

public class CatDao {
    public update(String name) {
        EntityManager em = HibernateUtil.getEntityManager();
        em.getTransaction().begin();
        Cat cat = em.find(Cat.class, 1L, LockModeType.PESSIMISTIC_WRITE);
        cat.setName(name);
        new Thread(()-> {
            EntityManager entityManager = HibernateUtil.getEntityManager();
            entityManager.getTransaction().begin();
            Cat updatedCat = entityManager.find(Cat.class, 1L);
            updatedCat.setName("Updated Cat");
            entityManager.getTransaction().commit();
        }).start();
        Thread.sleep(500);
        em.getTransaction().commit();
        em.clear();
    }
}

Output

select id , name, owner, version from Cat where id=1 for update
select id , name, owner, version from Cat where id=1
update CatLockVersion set name= Updated Cat, owner= Tim, version=1 where id=1 and version=0
update CatLockVersion set name= New, owner= Tim, version=1 where id=1 and version=0
select id , name, owner, version from Cat where id=1

Cat(id=1, name=Updated Cat, owner=Tim)