Spring JPA Lock

时间:2016-02-20 12:44:33

标签: java spring hibernate jpa

当谈到锁定弹簧jpa时,我真的很不安全 因此,请将此问题视为一个澄清。我真的希望我理解它是正确的,但我的英语对于理解复杂的博客文章并不是那么好 所以这就是我认为我从一些文章中得到的:

锁定有两种基本类型:

    计划较少的写操作时,应使用
  • 乐观。读操作不会锁定对象 例如:你有一个"货币余额"浮动在具有乐观锁定的实体中。现在两个进程读取此值并将其用于计算和填充。其中一个现在更改了值并使用更新将其写入数据库。这一点没有错误。
    但现在另一个进程也改变了价值并想要更新它。现在有一个错误。这只是因为第二次更新而发生的 如果第二个进程已经删除了实例,则不会发生任何事情。
  • 计划多次写入操作时,应使用
  • 悲观。读操作将锁定一个对象 例如:你有一个"货币余额"浮动在具有悲观锁定的实体中。一个过程使用" findOne"来读取数据/值 在此之后,另一个进程也想要读取数据,使用乐观锁可以实现什么,但是对于悲观锁,他现在必须等待(没有错误,只是等待)。
    当进程1准备就绪(更改值并更新它)时,进程2可以继续。

第一个问题:到目前为止这是否正确?当我现在想测试这个knollage时,我可以在这个LockModeType之间做出选择:

  • OPTIMISTIC
  • OPTIMISTIC_FORCE_INCREMENT
  • PESSIMISTIC_READ
  • PESSIMISTIC_WRITE
  • PESSIMISTIC_FORCE_INCREMENT

为什么现在有这么多子锁,他们做了什么?当" OPTIMISTIC"是我试图理解的顶级乐观锁,然后是" OPTIMISTIC_FORCE_INCEMENT"?
版本更新与此有何关系? (或@version?)

继续:
Spring jpa中有三种锁的基本用法:

  1. 在普通列上,例如:

    @Entity
    public class PersonEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Lock(LockModeType.PESSIMISTIC_WRITE)
        private String name;
    }  
    
  2. 在另一个表的外键上,例如:

    @Entity
    public class PersonEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Lock(LockModeType.PESSIMISTIC_WRITE)
        @OneToOne
        private Group group;
    }  
    
  3. 在存储库内的表上,例如:

    interface PersonRepository extends Repository<Person, Long> {
        @Lock(LockModeType.PESSIMISTIC_WRITE)
        Person findOne(Long id);
    }  
    
  4. 使用

    直接锁定实体
    @Entity
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    public class PersonEntity { }
    

    是不可能的。因此,您可以使用3(锁定在存储库中)。

    第二个问题:这是对的吗?我忘了使用锁定吗?

    继续:
    锁定背后的想法是,其他方法/进程必须等到锁被释放(除了乐观锁,这里抛出错误)。
    只要实例/对象处于活动状态或直到下一次提交,Lock就会存在 解锁对象有两种主要的可能性:

    1. 在交易中:在此完整方法中,锁定处于活动状态。但是当返回时,锁将被移除。

      @Transactional
      public void test(){
          System.out.println("start, but not locked yet");
          PersonEntity person1 = personRepository.findOne(1L); // locks this person or must wait, when locked
          System.out.println("now locked");
          // do something
          return true; // now the lock will be deleted -> unlocked again
      }  
      
    2. 直到删除对象:选择对象时数据将被锁定,删除对象时数据将被解锁。

      public void test(){
          System.out.println("start, but not locked yet");
          PersonEntity person1 = personRepository.findOne(1L); // locks this person or must wait, when locked
          System.out.println("now locked");
          // do something
          person1 = null; // now the lock will be deleted -> unlocked again
          System.out.println("not locked anymore");
          // do something more
          return true;
      }  
      
    3. 第三个问题:到目前为止这是正确的吗?这个功能真的可以等待数据被锁定吗?当对象设置为null时,是否可以删除锁?

      最后的话:
      我真的希望我不会惹恼别人。但就像我说的那样:我很难理解英语中这种复杂的结构:(
      非常感谢任何形式的帮助:)我真的很感激任何帮助。无论你是否给我链接以便更多地理解或直接回答我的问题:)

3 个答案:

答案 0 :(得分:4)

锁定

锁定用于防止脏读(读取未提交的数据)和不可重复的读取(读取在一次读取完成之前被另一个事务删除的数据)。

乐观

乐观锁定将在提交事务时锁定资源。乐观锁定将在第一次访问时保存数据的状态。因此,在乐观锁定中,您可以进行并发访问。如果要执行乐观操作,则在操作之前将初始状态与当前状态进行比较。如果存在差异(资源在此期间已被修改),则不会提交事务。

乐观的力量增量

对于版本化对象,这将增加对象的版本号。在非版本化对象上,将抛出异常。

悲观

悲观阅读

对于可重复读取,用于确保读取之间不更新数据。它是一个共享锁,意味着不同的进程可以执行读操作(不允许写操作)。

悲观写作

强制序列化更新的独占锁。乐观锁定只保存状态,这里它被锁定以防止在并发操作发生这种情况时发生事务失败/死锁。

悲观写入力增量

类似于它的乐观对手,一个更新对象版本的悲观写作。为非版本化对象引发异常。

答案 1 :(得分:0)

关于最后一点-删除一行是否会解除该行的锁定-我认为不会。删除本身只会在事务结束时提交;请注意,如果整个交易回滚,则删除也会回滚。

答案 2 :(得分:0)

我遇到了您遇到的所有这些问题,我正在发布一个示例,如果有帮助,我实际上会尝试过。我有一个设备实体,一个设备状态实体(与设备一对一),一个配置实体(与设备一对多)。我有两条用于更新设备状态的路径(通过mqtt消息处理程序和http请求)。

以下方法在我的DeviceStatus存储库中

@Lock(LockModeType.PESSIMISTIC_WRITE)
DeviceStatus findByDeviceMacAddress(String macAddress);    //used by the mqtt handler

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<DeviceStatus> findByDeviceConfigurationId(Long deviceConfigurationId);    //used by the http request handler

如果http请求处理程序首先调用findByDeviceConfigurationId,则将获取PESSIMISTIC_WRITE,直到事务完成为止,mqtt处理程序无法获取对findByDeviceMacAddress的PESSIMISTIC_WRITE锁(两个处理程序都试图检索相同的DeviceStatus实体)。这意味着对findByDeviceMacAddress的调用将等待,直到http请求处理程序完成事务为止。同样,如果mqtt处理程序是第一个调用findByDeviceMacAddress的处理程序,则http请求处理程序将不得不等待,直到mqtt处理程序完成其事务。

我知道这不是一个完整的答案,但认为这可能会对您有所帮助。