7 Temmuz 2019 Pazar

JPA EntityManager Pessimistic Lock

Giriş
Açıklaması şöyle.
In case of pessimistic locking, JPA creates a transaction that obtains a lock on the data until the transaction is completed. This prevents other transactions from making any updates to the entity until the lock is released.

Pessimistic locking can be very useful when the data is frequently accessed and modified by multiple transactions.

Keep in mind that using pessimistic locking may result in decreased application performance, if the entities are not susceptible to frequent modifications.
LockMode parametresi alan metodların imzası şöyle
<T> T	find(Class<T> entityClass, Object primaryKey, LockModeType lockMode)
<T> T	find(Class<T> entityClass, Object primaryKey, LockModeType lockMode,
  Map<String,Object> properties)

void lock(Object entity, LockModeType lockMode)
void lock(Object entity, LockModeType lockMode, Map<String,Object> properties)

void refresh(Object entity, LockModeType lockMode)
void refresh(Object entity, LockModeType lockMode, Map<String,Object> properties)
1. PESSIMISTIC_READ
Açıklaması şöyle. Ben okuma yaparken başkası da okuyabilir ancak ben başkasının yazmasını engellerim.
The pessimistic lock for read operations is only available in some database systems. When this strategy is not available for the current database, the JPA provider will issue a pessimistic write lock instead.
This operation will lock the record in a shared mode, meaning that other transactions may freely read the record and even lock that same record using an identical pessimist read strategy and still continue to process. Every write operation or write lock against the record will become locked by the database until no other transaction holds a lock to that record. Such write operations that are locked by the database while a transaction holds a read lock include UPDATE operations and SELECT ... FOR UPDATE operations. The semantics are similar to a shared read lock.
Örnek
Elimizde şöyle bir kod olsun
Post post = entityManager.find(Post.class, id, LockModeType.PESSIMISTIC_READ);
Üretilen SQL cümlesi şöyle
SELECT
    p.id AS id1_0_0_,
    p.slug AS slug2_0_0_,
    p.title AS title3_0_0_
FROM
    post p
WHERE
    p.id = 1
FOR SHARE
2. PESSIMISTIC_WRITE
Açıklaması şöyle. Ben yazma yaparken başkası ne yazabilir ne de okuyabilir.
This instruction will be translated into a SELECT ... FOR UPDATE statement:
Veritabanı seviyesinde satır için kilit atar. Açıklaması şöyle.
The record will become locked for the duration of the current transaction. Any UPDATE operation, SELECT ... FOR UPDATE operation, or pessimistic READ lock operation executed against that same record by other transaction will become locked at the database level until the current transaction - that holds the lock - completes. The semantics are similar to an exclusive write lock.
Örnek
Şöyle yaparız.
MyEntity e = repo.findByName(name)
              .orElseThrow(() -> new ResourceNotFoundException(name));
try {
  entityManager.lock(e, LockModeType.PESSIMISTIC_WRITE)
} catch ( PessimisticLockException e) {
    //Handle somehow
}
repo.delete(e);
Örnek
Şöyle yaparız.
User user = entityManager.find(User.class,id);
entityManager.lock(user,LockModeType.PESSIMISTIC_WRITE);
user.getOrders().forEach(order -> {

  orderRepository.delete(order);
 });
...
Örnek
Şöyle yaparız
@Service
public class MyService {

  private final EntityManager entityManager;

  public MyService(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  @Transactional
  public void updateRecordPessimistic(Long recordId) {
    Record record = entityManager.find(Record.class, recordId,
      LockModeType.PESSIMISTIC_WRITE);

    // Perform the update operation on the record
    record.setName("New Name");
    entityManager.merge(record);
  }
}
Aynı şeyi SpringBoot ile şöyle yaparız
@Entity
public class Record {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;

  // getters and setters
}

@Repository
public interface RecordRepository extends JpaRepository<Record, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Optional<Record> findById(Long id);
}

@Service
public class MyService {

  private final RecordRepository recordRepository;

  public MyService(RecordRepository recordRepository) {
    this.recordRepository = recordRepository;
  }

  @Transactional
  public void updateRecordSpringDataPessimistic(Long recordId) {
    Record record = recordRepository.findById(recordId).orElseThrow();
    record.setName("New Name");
    recordRepository.save(record);
  }
}
3. PESSIMISTIC_FORCE_INCREMENT 
Açıklaması şöyle.
This lock mode will result in a pessimistic write lock (SELECT ... FOR UPDATE) and will make sure that an eventually existing entity version field is incremented, even if the entity being locked is not changed by the current transaction which acquired the lock.

Hiç yorum yok:

Yorum Gönder