更新 Hibernate 中每行的时间戳

Mat*_*att 7 java postgresql timestamp hibernate jpa

我的 Postgres 数据库中有一个表,其中有一个时间戳列。我希望每次更新行时都会自动插入它。我写了一个数据库触发器:

CREATE FUNCTION update_last_edit_date() RETURNS trigger AS $update_last_edit_date$
                BEGIN
                    NEW.last_edit_date := localtimestamp(0);
                    RETURN NEW;
                END;
            $update_last_edit_date$ LANGUAGE plpgsql;


 CREATE TRIGGER update_last_edit_date BEFORE UPDATE ON employee
            FOR EACH ROW
            WHEN (OLD.* IS DISTINCT FROM NEW.*)
            EXECUTE PROCEDURE update_last_edit_date();
Run Code Online (Sandbox Code Playgroud)

效果很好,但我想知道是否有一种更简单的方法可以使用 jpa/hibernate 注释来做到这一点。我尝试了这些不同的选项:

@预更新

@PreUpdate
    private void onUpdate(){
        this.lastEditDate = new Date();
    }
Run Code Online (Sandbox Code Playgroud)

@更新时间戳

@UpdateTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastEditDate;
Run Code Online (Sandbox Code Playgroud)

但我得到的是,当我更新一行时,所有行的时间戳都会更新,因此表中的所有时间戳始终相同。我在这里做错了什么?

Vla*_*cea 5

有很多方法可以实现这一目标。

@EntityListener

您可以存储@Embeddable审计属性:

@Embeddable
public class Audit {
 
    @Column(name = "created_on")
    private LocalDateTime createdOn;
 
    @Column(name = "updated_on")
    private LocalDateTime updatedOn;
 
    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

这需要一个EntityListener如下所示的:

public class AuditListener {
 
    @PrePersist
    public void setCreatedOn(Auditable auditable) {
        Audit audit = auditable.getAudit();
 
        if(audit == null) {
            audit = new Audit();
            auditable.setAudit(audit);
        }
 
        audit.setCreatedOn(LocalDateTime.now());
    }
 
    @PreUpdate
    public void setUpdatedOn(Auditable auditable) {
        Audit audit = auditable.getAudit();
 
        audit.setUpdatedOn(LocalDateTime.now());
    }
}
Run Code Online (Sandbox Code Playgroud)

您的实体必须实现该Audit接口:

public interface Auditable {
 
    Audit getAudit();
 
    void setAudit(Audit audit);
}
Run Code Online (Sandbox Code Playgroud)

实体将如下所示:

@Entity(name = "Tag")
@Table(name = "tag")
@EntityListeners(AuditListener.class)
public class Tag implements Auditable {
 
    @Id
    private String name;
 
    @Embedded
    private Audit audit;
 
    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常优雅的解决方案,因为它从主实体映射中提取审核逻辑。

@PrePersist@PreUpdate

您也可以使用@PrePersist和JPA 注释:@PreUpdate

@Embeddable
public class Audit {
 
    @Column(name = "created_on")
    private LocalDateTime createdOn;
     
    @Column(name = "updated_on")
    private LocalDateTime updatedOn;
 
    @PrePersist
    public void prePersist() {
        createdOn = LocalDateTime.now();
    }
 
    @PreUpdate
    public void preUpdate() {
        updatedOn = LocalDateTime.now();
    }
 
    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

并将可嵌入添加Audit到实体中,如下所示:

@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
 
    @Id
    private String name;
 
    @Embedded
    private Audit audit = new Audit();
 
    //Getters and setters omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

Hibernate 特定的@CreationTimestamp@UpdateTimestamp

@CreationTimestamp
@Column(name = "created_on")
private Date createdOn;

@Column(name = "updated_on")
@UpdateTimestamp
private Date updatedOn;
Run Code Online (Sandbox Code Playgroud)

就是这样!

现在,与您的评论相关:

但我得到的是,当我更新一行时,所有行的时间戳都会更新,因此表中的所有时间戳始终相同。我在这里做错了什么?

时间戳只会更新被修改的实体,而不是所有行。当仅修改单行时,更新所有行的时间戳是没有任何意义的。否则,为什么要在行本身上放置该列?

如果您想要最后修改时间戳,只需运行如下查询:

SELECT MAX(updated_on)
FROM tags
Run Code Online (Sandbox Code Playgroud)