触发器与JPA @PrePersist创建和更新时间戳的优缺点

时间:2011-03-09 16:39:15

标签: java hibernate postgresql jpa jpa-2.0

我正在构建一个新的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应用程序访问数据库,则强制执行保留这两列的要求。
  3. 选项A缺点:

    1. 在插入和更新后必须执行选择以读取触发器放置的值。
    2. 我正在使用hibernate注释来回读值
    3. 选项B专业人士:

      1. 创建DDL时输入较少
      2. 插入和更新后无需回读数据库中的值
      3. 纯JPA注释没有hibernate特定注释
      4. 选项B缺点:

        1. 群集中时钟偏差的危险
        2. 每当JPA提供程序决定调用不可预测的回调方法时设置的字段
        3. 如何为新应用程序解决此问题,您可以完全控制数据库和java代码。

3 个答案:

答案 0 :(得分:6)

  

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

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

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

如果这只是为了方便前端(并且它可以优雅地处理因错误值而导致的冲突),那么JPA方法可能更容易维护。

答案 1 :(得分:2)

我目前正在以下列方式使用选项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 :(得分:1)

  

群集中时钟偏差的危险

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

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