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

时间:2016-03-04 02:16:08

标签: java spring locking

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

1 个答案:

答案 0 :(得分:13)

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

  

一般策略

  1. 开始交易。
  2. 使用SELECT FOR UPDATE加载父行。
  3. 更新儿童。
  4. 拯救儿童。
  5. 提交交易。这将保存子项并释放父行上的锁。
  6. 您可以使用Spring Transactions来强制执行事务边界。以下内容将起作用:

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

    @Transactional将围绕对someMethod的调用应用事务语义。请注意,public的方法必须为@Transactional才能生效。

    执行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);
    }
    

    将完成这项工作。

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

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