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