Spring中的写锁实体来自数据库

Ale*_*one 3 java spring locking

当我正在使用他的子实体时,我需要对父实体进行写锁定,以便不允许修改(或者稀疏)父实体.我需要使用Spring并直接从数据库执行锁定(以避免在集群中执行应用程序时出现问题).

man*_*ish 14

为了实现您正在寻找的策略,您需要SELECT FOR UPDATE在父行上触发SQL查询(例如,SELECT * FROM parent WHERE id = ? FOR UPDATE.这将获取对SELECT查询提取的行的锁定.

一般战略

  1. 开始交易.
  2. 使用a加载父行SELECT FOR UPDATE.
  3. 更新孩子.
  4. 拯救孩子.
  5. 提交交易.这将保存子项并释放父行上的锁.

您可以使用Spring Transactions来强制执行事务边界.以下内容将起作用:

class SomeService {
  @Transactional
  public ... someMethod(...) {
    // Load the parent row using SELECT FOR UPDATE.

    // Save children.
  }
}
Run Code Online (Sandbox Code Playgroud)

@Transactional将围绕调用应用事务语义someMethod.请注意,该方法必须是public为了@Transactional工作.


执行a SELECT FOR UPDATE取决于您访问数据库的准确程度 - Spring JDBC,Spring ORM,Spring Data JPA等.以下是使用这些库实现此目的的方法:

Spring JDBC

您只需SELECT FOR UPDATE使用JdbcTemplate该类执行查询即可. jdbcTemplate.execute("SELECT * FROM parent WHERE row = ? FOR UPDATE")应该管用.

春天ORM

您必须使用特定于ORM的模板类来强制执行锁定模式.例如,使用Hibernate4 HibernateTemplate即可hibernateTemplate.get(Class<T> entityType, Serializable id, LockMode lock).

Spring Data JPA

您可以@Lock(LockModeType.PESSIMISTIC_WRITE)在执行查询时使用强制执行悲观锁定来注释存储库方法.例如

interface ParentRepository extends CrudRepository<Parent, Long> {
  @Lock(LockModeType.PESSIMISTIC_WRITE)
  Parent findOne(Long id);
}
Run Code Online (Sandbox Code Playgroud)

会做的.


您应该注意的一件事是,如果同时调用此操作太多次,您将遇到超时并且可能也会出现死锁,因为行被排他性地锁定.您将从以下方面受益:

  1. 保持锁只很短的时间,可能是在处理的最后阶段,确保预先执行任何验证等.这将确保快速释放锁.
  2. 与并发用户一起测试,并了解超时和死锁可能发生的频率.如果您看到超时和死锁并且不希望用户重试,您可以使用Spring Retry项目提供的功能来重试方法.