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

发布于 2024-10-21 15:56:27 字数 2055 浏览 1 评论 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 应用程序访问数据库,则强制要求保留这两列。

选项 A 缺点:

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

选项 B 优点:

  1. 创建 DDL 时输入更少
  2. 插入和更新后无需从数据库读回值
  3. 纯 JPA 注释 无 Hibernate 特定注释

选项 B 缺点:

  1. 存在时钟偏差的危险cluster
  2. 每当 JPA 提供者决定调用回调方法时设置的字段是不可预测的

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

I am building a new web app and I am using Spring, JPA/Hibernate, and Postgres. Some of my tables have creation_ts and lastupdate_ts columns which are timestamp columns that track when an insert occurred and when the last update occurred on a row.

I am also using a naming convention for columns in my tables so as a matter of design policy every table is guaranteed to have two columns pkey which is an integer surrogate key, and version for optimistic locking.

I have two ways to keep these fields up to date.

Option A: use triggers

This is the solution I have in place right now, I have two Postgres trigger that fire on insert and update and will keep these fields up to date. and I have two classes.

@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;
    }
}

and I have

@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;
    }
}

Option B: Use JPA listeners

In this option I would use JPA listeners to keep to timestamp columns up-to-date.

My Question:

Which of those two approaches is better? As I see things here is my personal list of pros and cons of each option and I very interested from hearing the experience of others with these two choices.

Option A pros:

  1. Database is doing the updates with the triggers so there is no danger of having clock skew in the cluster running the web app.
  2. If a non-JPA application accesses the database the requirement to keep those two columns is enforced.

Option A cons:

  1. Have to do a select after the insert and update to read the values that the triggers put into place.
  2. I am using hibernate annotations to read back the values

Option B pros:

  1. Less typing when creating the DDL
  2. No need to read back values from the database after insert and update
  3. Pure JPA annotations no hibernate specific annotations

Option B cons:

  1. Danger of clock skew in the cluster
  2. Fields set whenever the JPA provider decides to call the callback methods not predictable

How you would solve this problem for a new app where you have total control over the database and the java code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

海的爱人是光 2024-10-28 15:56:27

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

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

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

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

Have to do a select after the insert and update to read the values that the triggers put into place.

You can use INSERT ... RETURNING or UPDATE ... RETURNING to retrieve the values that were changed by the trigger, so there is no need to do another SELECT.

Apart from that, I'd say it depends on your environment. If the application is mission critical and will fail miserably if those columns aren't maintained correctly, then I'd stick with the triggers.

If this is only for convenience in the front end (and it can handle conflicts due to incorrect values gracefully), then the JPA approach is probably easier to maintain.

抽个烟儿 2024-10-28 15:56:27

我目前正在按以下方式使用选项 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 回调,但我可以说,仅在特定对象上设置日期看起来太费力了。

I'm currently using Option A (with all your frameworks and PostgreSQL as well) in the following manner:

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

If you use the columnDefinition as written in the code you won't have to select the object again neither write any code to set the date on your objects. I've never used the JPA callbacks other than to connect Envers framework but I can say it looks too much effort only to set the date on specific objects.

掩耳倾听 2024-10-28 15:56:27

集群中存在时钟偏差的危险

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

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

Danger of clock skew in the cluster

I'd say not to worry about it. It can fail in the cluster just like it could fail in the database. In a proper production environment, you'd have your own NTP servers, and your cluster would sync with that.

I usually prefer to keep everything in Java, as a centralized place for "logic" is desirable (for me). Placing logic in triggers is acceptable if you have non-Java applications consuming it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文