[Java] 使用Hibernate和MySQL创建时间戳和上次更新时间戳


Answers

本文中的资源以及来自不同来源的左右信息,我带着这个优雅的解决方案,创建了以下抽象类

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@MappedSuperclass
public abstract class AbstractTimestampEntity {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created", nullable = false)
    private Date created;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated", nullable = false)
    private Date updated;

    @PrePersist
    protected void onCreate() {
    updated = created = new Date();
    }

    @PreUpdate
    protected void onUpdate() {
    updated = new Date();
    }
}

并让所有实体扩展它,例如:

@Entity
@Table(name = "campaign")
public class Campaign extends AbstractTimestampEntity implements Serializable {
...
}
Question

对于某个Hibernate实体,我们需要存储它的创建时间和上次更新的时间。 你会如何设计?

  • 你会在数据库中使用哪些数据类型(假设MySQL可能与JVM在不同的时区)? 数据类型是否可以识别时区?

  • 你将在Java中使用哪些数据类型( DateCalendarlong ,...)?

  • 你会为谁设置时间戳 - 数据库,ORM框架(Hibernate)还是应用程序员?

  • 你会用什么注释来进行映射(例如@Temporal )?

我不仅在寻找一个可行的解决方案,而且还需要一个安全和设计良好的解决方案。




作为JAVA中的数据类型,我强烈建议使用java.util.Date。 使用日历时,我遇到了非常讨厌的时区问题。 看到这个Thread

为了设置时间戳,我建议使用AOP方法,或者你可以简单地在表上使用触发器(实际上,这是我唯一能够使用触发器的东西)。




我们有类似的情况。 我们正在使用Mysql 5.7。

CREATE TABLE my_table (
        ...
      updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    );

这对我们有效。




一个好的方法是为所有的实体设置一个通用的基类。 在这个基类中,如果你的id属性在你的所有实体(一个通用设计)中共同命名,你的创建和最后一次更新日期属性,你可以拥有你的id属性。

对于创建日期,您只需保留一个java.util.Date属性。 请确保始终使用新的Date()初始化它。

对于上次更新字段,您可以使用Timestamp属性,您需要使用@Version将其映射。 通过这个Annotation,这个属性将会被Hibernate自动更新。 请注意,Hibernate也会应用乐观锁定(这是一件好事)。




借助Olivier的解决方案,在更新声明期间,您可能遇到以下情况:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:'created'列不能为空

为了解决这个问题,将updatable = false添加到“created”属性的@Column注释中:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;



您也可以使用拦截器来设置值

创建您的实体实现的名为TimeStamped的接口

public interface TimeStamped {
    public Date getCreatedDate();
    public void setCreatedDate(Date createdDate);
    public Date getLastUpdated();
    public void setLastUpdated(Date lastUpdatedDate);
}

定义拦截器

public class TimeStampInterceptor extends EmptyInterceptor {

    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, 
            Object[] previousState, String[] propertyNames, Type[] types) {
        if (entity instanceof TimeStamped) {
            int indexOf = ArrayUtils.indexOf(propertyNames, "lastUpdated");
            currentState[indexOf] = new Date();
            return true;
        }
        return false;
    }

    public boolean onSave(Object entity, Serializable id, Object[] state, 
            String[] propertyNames, Type[] types) {
            if (entity instanceof TimeStamped) {
                int indexOf = ArrayUtils.indexOf(propertyNames, "createdDate");
                state[indexOf] = new Date();
                return true;
            }
            return false;
    }
}

并在会话工厂注册