【问题标题】:Triggers vs. JPA @PrePersist for creation and update timestamps pros and cons触发器与 JPA @PrePersist 用于创建和更新时间戳的优缺点
【发布时间】:2011-07-12 01:32:08
【问题描述】:

我正在构建一个新的 Web 应用程序,并且正在使用 Spring、JPA/Hibernate 和 Postgres。我的一些表有 creation_ts 和 lastupdate_ts 列,它们是时间戳列,用于跟踪何时发生插入以及何时发生最后一次更新。

我还对表中的列使用了命名约定,因此作为设计策略,每个表都保证有两列 pkey,它是一个整数代理键,以及用于乐观锁定的版本。

我有两种方法可以使这些字段保持最新状态。

选项 A:使用触发器

这是我现在采用的解决方案,我有两个 Postgres 触发器,它们在插入和更新时触发,并将使这些字段保持最新。我有两节课。

@MappedSuperclass
public abstract class PersistableObject
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="pkey")
    private Integer pkey;

    @Version
    @Column(name="version")
    private Integer version;

    public Integer getPkey()
    {
        return this.pkey;
    }

    public Integer getVersion()
    {
        return this.version;
    }
}

我有

@MappedSuperclass
public class TimeStampedPersistableObject extends PersistableObject {

    @Column(name = "creation_ts")
    @Temporal(TemporalType.DATE)
    @org.hibernate.annotations.Generated(value = GenerationTime.INSERT)
    private Date    creationTimestamp;

    @Column(name = "update_ts")
    @Temporal(TemporalType.DATE)
    @org.hibernate.annotations.Generated(value = GenerationTime.ALWAYS)
    private Date    updateTimestamp;

    public Date getCreationTimestamp()
    {
        return this.creationTimestamp;
    }

    public Date getUpdateTimestamp()
    {
        return this.updateTimestamp;
    }
}

选项 B:使用 JPA 侦听器

在此选项中,我将使用 JPA 侦听器来使时间戳列保持最新。

我的问题:

这两种方法哪一种更好?正如我所看到的,这里是我个人列出的每个选项的优缺点,我很感兴趣听到其他人对这两个选项的体验。

选项 A 优点:

  1. 数据库正在使用触发器进行更新,因此运行 Web 应用程序的集群中不会出现时钟偏差的危险。
  2. 如果非 JPA 应用程序访问数据库,则强制要求保留这两列。

选项 A 的缺点:

  1. 必须在插入和更新后进行选择以读取触发器放置的值。
  2. 我正在使用休眠注释来回读值

选项 B 优点:

  1. 创建 DDL 时减少输入
  2. 插入和更新后无需从数据库中读回值
  3. 纯 JPA 注释没有特定于休眠的注释

选项 B 的缺点:

  1. 集群中时钟偏差的危险
  2. JPA 提供者决定调用不可预测的回调方法时设置的字段

对于一个您可以完全控制数据库和 java 代码的新应用程序,您将如何解决这个问题。

【问题讨论】:

标签: java hibernate postgresql jpa jpa-2.0


【解决方案1】:

我目前正在以下列方式使用选项 A(以及您的所有框架和 PostgreSQL):

@Column(insertable = false, updatable = false, nullable = false, columnDefinition = "timestamp without time zone default CURRENT_TIMESTAMP")
@Generated(GenerationTime.INSERT)
public Date getCreatedIn() {
    return createdIn;
}

如果您使用代码中编写的 columnDefinition,则无需再次选择对象,也无需编写任何代码来设置对象的日期。除了连接 Envers 框架外,我从未使用过 JPA 回调,但我可以说仅在特定对象上设置日期看起来太费力了。

【讨论】:

    【解决方案2】:

    集群中时钟偏差的危险

    我会说不用担心。它可以在集群中失败,就像它在数据库中失败一样。在适当的生产环境中,您将拥有自己的 NTP 服务器,并且您的集群将与之同步。

    我通常更喜欢将所有内容都保存在 Java 中,因为“逻辑”的集中位置是可取的(对我而言)。如果您有非 Java 应用程序在使用它,那么将逻辑放置在触发器中是可以接受的。

    【讨论】:

      【解决方案3】:

      必须在插入和更新后进行选择以读取触发器放置的值。

      您可以使用INSERT ... RETURNINGUPDATE ... RETURNING 来检索触发器更改的值,因此无需再执行一次SELECT。

      除此之外,我想说这取决于您的环境。如果应用程序是关键任务并且如果没有正确维护这些列将会惨遭失败,那么我会坚持使用触发器。

      如果这只是为了前端的方便(并且它可以优雅地处理由于不正确的值引起的冲突),那么 JPA 方法可能更容易维护。

      【讨论】:

      • 您能否提供一些INSERT ... RETURNINGUPDATE ... RETURNING 的详细信息?我在哪里可以找到有关这些的信息?
      • @AskarKalykov:参见手册中的示例:postgresql.org/docs/current/static/sql-insert.html#AEN78304
      • 好吧,我想如果它是 JPA 功能。在 JPA 中找到这种方式:do x = em.merge(x); em.flush(); em.reload(x);
      猜你喜欢
      • 2014-09-30
      • 2021-06-26
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 2012-02-11
      • 2016-11-04
      • 2011-03-30
      • 2010-09-18
      相关资源
      最近更新 更多