带有来自数据库

时间:2018-05-30 13:45:25

标签: java spring hibernate spring-data-jpa

我有一个Spring应用程序支持H2和SqlServer。 我已经设置了实体列,例如:

@Column(insertable = false, columnDefinition = "bigint default CURRENT_TIMESTAMP")

现在,当我保存新实体时,时间戳设置正确。但是,当我修改实体时,时间戳将是我在对象预更新上设置的任何内容。 我知道如果我设置updatable = false然后它将再次更新为current_timestamp但是我需要灵活性以便只有在有重大变化时才设置时间戳,IE如果我更改实体上的注释字段我不想要时间戳要更改,但如果我更改所有者字段,我确实希望更新时间戳。

我已经尝试将值设置为null但这没有任何作用。我不知道如何使用@PreUpdate方法从db获取CURRENT_TIMESTAMP,我也不知道如何轻松修改crud.save()。我能想到的唯一选择是使用@Modifying添加一个带有本机查询的customSave,但这是最后的选择。

注意:我必须使用数据库时间戳,准确性非常重要,行必须按递增时间戳顺序保留,服务器时间漂移会引入竞争条件。

2 个答案:

答案 0 :(得分:1)

根据您的要求列表,您实际上有3个选项

  1. 使用表上的数据库触发器来控制更新时间戳值。
  2. 使用@PreUpdate回调。
  3. 当您有权访问更新状态和现有状态时,请将此作为服务的一部分。
  4. 选项(1)非常简单,因为您在数据库触发器中同时具有当前状态和更新状态,并且您可以执行任何必要的操作来确定是应该设置值还是取现时的现有值更新。

    (1)和(2)之间唯一的缺点是(2)将基于数据库时间而是基于应用程序时间,因为您将在应用程序服务器上执行此操作。但是(2)的好处是,您的代码将与数据库无关,允许您在Hibernate支持的任何数据库平台上的任何位置进行部署,或者无需任何顾虑即可使用正确的方言。

    我的个人意见,选择(2),因为它也非常注重领域驱动设计。

    对于(2)的实施,这是非常简单的

    @Entity
    public class YourSuperDumperEntity {
    
      // Clear this value on load
      @PostLoad
      public void postLoadClearFlags() {
        fieldValuesChangedThatRequireTimestampUpdate = false;
      }
    
      // This handles the setting of the value at update-time
      @PreUpdate
      public void preUpdateCallbackAdjustTimestampOnChanges() {
        if ( fieldValuesChangedThatRequireTimestampUpdate ) {
          updateTimestamp = new Date();
        }
      }
    
    
      public void setSomeFieldThatTriggersUpdateTimestamp(String value) {
        // check if the old and new value differ enough to trigger update date
        // if so, set the boolean flag here as follows
        this.fieldValuesChangedThatRequireTimestampUpdate = true;
    
        // now set the value
        this.someFieldThatTriggersUpdateTimestamp = value;
      }
    }
    

    正如我之前所说,这里的美丽是它的所有逻辑都是自包含在它所属的单个类中,因为你所谈论的是实体状态。

    您可能需要更多地使用JPA回调以满足您的需求,但这个想法仍然相同。

    如果您不喜欢(2),那么我的建议是(3)因为它维护了数据库不可知的支持,我发现它比从数据库本身获取的时间戳更重要。通过适当的操作系统时间同步,实际的时间源应该是无关紧要的。

答案 1 :(得分:0)

感谢上面提出的建议,我今天大部分时间都在努力解决这个问题,没有简单的解决方案,Naros你的触发建议最接近可行但是事实证明,无论我发现什么时间戳的来源,除非我启用读取uncommited这不是一个选项,有一个问题,事务可能需要几百毫秒,因此有一个太大的竞争条件来信任时间戳顺序,因为它是在保存而不是事务完成时分配的。 我已经选择添加一个新表,它将像队列一样,每次更新时间戳时都会将主表中的ID放入其中,效率较低但是在没有竞争条件的情况下是事务安全的。