【问题标题】:CRUD.save() with timestamp from DB带有数据库时间戳的 CRUD.save()
【发布时间】:2018-05-31 07:42:47
【问题描述】:

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

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

现在,当我保存新实体时,时间戳设置正确。但是,当我修改实体时,时间戳将是我在对象更新前设置的任何内容。 我知道如果我设置 updatable = false 那么它将再次更新为 current_timestamp 但是我需要灵活性以便仅在有重大更改时才设置时间戳,即如果我更改我不想要的实体上的评论字段要更改的时间戳,但如果我更改所有者字段,我确实希望更新时间戳。

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

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

【问题讨论】:

  • 如果您使用时间戳来影响某些顺序,我认为代理身份/序列主键可以很好地解决该问题,并且会自动进行 b-tree 索引,从而可以根据需要进行快速排序。正如我的回答所表明的那样,这允许时间戳行为基于业务需求,而不是试图改进同一领域的可能 2 个可能不相关的需求组。

标签: java spring hibernate spring-data-jpa


【解决方案1】:

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

  1. 在表上使用数据库触发器来控制更新时间戳值。
  2. 使用@PreUpdate 回调。
  3. 当您有权访问更新状态和现有状态时,将此作为您服务的一部分。

选项 (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),因为这维护了与数据库无关的支持,我发现它比源自数据库本身的时间戳重要得多。通过适当的操作系统时间同步,时间的实际来源应该是无关紧要的。

【讨论】:

    【解决方案2】:

    感谢上面的建议,我今天大部分时间都在与这个问题作斗争,但没有简单的解决方案,Naros 你对触发器的建议最接近可行,但事实证明,除非我启用读取,否则无论我找到的时间戳的来源是什么uncommited 这不是一个选项存在一个问题,即事务可能需要数百毫秒,因此竞争条件太大而无法信任时间戳顺序,因为它是在保存而不是事务完成时分配的。 我选择添加一个新表,它的作用类似于队列,并在每次更新时间戳时将主表中的 ID 放入其中,效率较低,但它是事务安全的,没有竞争条件。

    【讨论】:

      猜你喜欢
      • 2012-05-23
      • 1970-01-01
      • 2012-11-03
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多