Locking

楽観的ロック (Optimistic)

制約を違反するトランザクションでは、OptimisticLockExceptionがスローされる

LockModeType.OPTIMISTIC

perform a version check on locked Entity before commit

LockModeType.OPTIMISTIC_FORCE_INCREMENT

perform a version check on locked Entity before commit

force an increment to the version at the end of the transaction

import javax.persistence.Version;

@MappedSuperclass

public abstract class BaseEntity implements Serializable {

...

@Version

private Integer version;

...

}

悲観的ロック (Pessimistic)

制約を違反するトランザクションでは、PessimisticLockExceptionがスローされる

SELECT ... FOR UPDATEに相当

LockModeType.PESSIMISTIC_READ

lock the database row when reading

allow reads from other transactions (a shared lock)

LockModeType.PESSIMISTIC_WRITE

lock the database row when reading

do not allow reads, updates or deletes from other transactions (an exclusive lock)

LockModeType.PESSIMISTIC_FORCE_INCREMENT

lock the database row when reading

force an increment to the version at the end of the transaction (if present)

★Pessimistic Lock Timeout

方法1:Persistence unit (persistence.xml)

<properties>

<property name="javax.persistence.lock.timeout" value="1000"/>

</properties>

方法2:EntityManagerFactory

Map<String, Object> properties = new HashMap<String, Object>();

properties.put("javax.persistence.lock.timeout", 1000);

EntityManagerFactory emf =

Persistence.createEntityManagerFactory("xxx", properties);

方法3:EntityManager

EntityManager em = emf.createEntityManager(properties);

あるいは

em.setProperty("javax.persistence.lock.timeout", 1000);

方法4:Querying

Employee employee = em.find(

Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE, properties);

あるいは

em.refresh(employee, LockModeType.PESSIMISTIC_WRITE, properties);

★サンプル

@ApplicationScoped

public class CustomerDao {

@PersistenceContext

private EntityManager em;

public void updateName(String id, String name) {

Customer customer = em.find(Customer.class, id);

em.lock(customer,LockModeType.OPTIMISTIC_FORCE_INCREMENT);

em.flush(); // version++

customer.setName(name);

}

public Customer findById(String id) {

return em.find(Customer.class, id, LockModeType.OPTIMISTIC);

}

public List<Customer> findByName(String name) {

Query query = em.createQuery("SELECT...")

.setParameter("name", name);

query.setLockMode(LockModeType.PESSIMISTIC_FORCE_INCREMENT);

return query.getResultList();

}

}

@Entity

@NamedQuery(name = "findByName",

query = "SELECT...",

lockMode = LockModeType.PESSIMISTIC_FORCE_INCREMENT)

public class Customer extends BaseEntity {

...

}

Read then lock risk of stale data (cause OptimisticLockException)

Account account = em.find(Account.class, accountId);

em.lock(account, LockModeType.PESSIMISTIC_WRITE);

int balance = account.getBalance();

account.setBalance(balance - 100);

Read and lock lock longer (cause bottleneck, deadlock)

Account account =

em.find(Account.class, accountId, LockModeType.PESSIMISTIC_WRITE);

int balance = account.getBalance();

account.setBalance(balance - 100);

Read then lock and refresh

Account account = em.find(Account.class, accountId);

// Return the refreshed and locked object

em.refresh(account, LockModeType.PESSIMISTIC_WRITE);

int balance = account.getBalance();

account.setBalance(balance - 100);