如何自动生成创建或修改的时间戳字段?

时间:2011-03-10 09:29:51

标签: hibernate orm jpa

我的实体类:

@Entity
@Table(name = "user")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @SequenceGenerator(name = "USER_ID_GENERATOR", sequenceName = "USER_SEQ")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_ID_GENERATOR")
    @Column(name = "user_id")
    private long userId;


    @Temporal(TemporalType.DATE)
    private Date created;

    @Temporal(TemporalType.DATE)
    private Date modified;

    //setters and getters...
}

我希望CREATED和MODIFIED字段在您创建或修改对象时自动相互补充。 CREATED和MODIFIED字段应为TIMESTAMP类型。

我如何实现这一目标?

8 个答案:

答案 0 :(得分:32)

在4.3使用JPA的Hibernate中,可以直接在日期字段中使用“@CreationTimestamp”和“@UpdateTimestamp”

CreationTimestamp java doc

UpdateTimestamp java doc

答案 1 :(得分:17)

您只需在创建实例时创建new Date(),然后在实体更新时更新updated字段:

private Date created = new Date();
private Date updated = new Date();

@PreUpdate
public void setLastUpdate() {  this.updated = new Date(); }

不要为这些方法中的任何一个提供setter,只提供getter。

答案 2 :(得分:13)

我们使用PreInsertEventListener和PreUpdateEventListener执行此操作:

public class TracabilityListener implements PreInsertEventListener,PreUpdateEventListener {
    private void setPropertyState(Object[] propertyStates, String[] propertyNames,String propertyName,Object propertyState) {
        for(int i=0;i<propertyNames.length;i++) {
            if (propertyName.equals(propertyNames[i])) {
                propertyStates[i]=propertyState;
                return;
            }
        }
    }
    private void onInsert(Object entity,Object[] state, String[] propertyNames) {
        if (entity instanceof DomainObject) {
            DomainObject domainObject = (DomainObject) entity;
            Date date=new Date();
            domainObject.setDateCreation(date);
            setPropertyState(state, propertyNames, "dateCreation", date);
            domainObject.setDateModification(date);
            setPropertyState(state, propertyNames, "dateModification", date);
        }
    }

    private void onUpdate(Object entity,Object[] state, String[] propertyNames) {
        if (entity instanceof DomainObject) {
            DomainObject domainObject = (DomainObject) entity;
            Date date=new Date();
            setPropertyState(state, propertyNames, "dateCreation", domainObject.getDateCreation());
            domainObject.setDateModification(date);
            setPropertyState(state, propertyNames, "dateModification", date);
        }
    }

    @Override
    public boolean onPreInsert(PreInsertEvent event) {
        onInsert(event.getEntity(), event.getState(), event.getPersister().getPropertyNames());
        return false;
    }

    @Override
    public boolean onPreUpdate(PreUpdateEvent event) {
        onUpdate(event.getEntity(), event.getState(), event.getPersister().getPropertyNames());
        return false;
    }
}

但如果您希望属性为时间戳,则应使用

进行注释
@Temporal(TemporalType.TIMESTAMP)

答案 3 :(得分:11)

你可以使用Spring Data JPA,Spring可以在你的字段上使用注释@CreatedBy,@ CreatedDate,@ LastModifiedBy,@ LastModifiedDate轻松实现。您可以按照以下简单示例

// Will need to enable JPA Auditing
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
class JpaConfig {
    // Creating a bean of AuditorAwareImpl which will provide currently logged in user
    @Bean
    public AuditorAware<String> auditorAware() {
        return new AuditorAwareImpl();
    }
}

// Moving necessary fields to super class and providing AuditingEntityListener entity listner class
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
abstract class Auditable<U> {

    @CreatedBy
    protected U createdBy;

    @CreatedDate
    @Temporal(TIMESTAMP)
    protected Date createdDate;

    @LastModifiedBy
    protected U lastModifiedBy;

    @LastModifiedDate
    @Temporal(TIMESTAMP)
    protected Date lastModifiedDate;

    // Getters and Setters
}

// Creating implementation of AuditorAware and override its methods to provide currently logged in user
class AuditorAwareImpl implements AuditorAware<String> {

    @Override
    public String getCurrentAuditor() {
        return "Naresh";
        // Can use Spring Security to return currently logged in user
        // return ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
    }
}

@Entity
class File extends Auditable<String> {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private String content;

    // Getters and Setters
} 

您可以在我的文章Spring Data JPA Auditing: Saving CreatedBy, CreatedDate, LastModifiedBy, LastModifiedDate automatically上阅读更多详情。

答案 4 :(得分:2)

由于这是一个常见问题,并且通过搜索可以获得很多半生的解决方案,让我介绍一下我所确定的内容:

  1. 定义两个简单的字段注释,@CreatedDate@ModifiedDate;
  2. 使用它们来注释您实体上的相应字段;
  3. 使用Traceability监听器注册实体类,如下所示。
  4. 这就是你的实体类的样子:

    @EntityListeners(Traceability.class)
    public class MyEntity {
      @CreatedDate @Temporal(TIMESTAMP) public Date created;
      @ModifiedDate @Temporal(TIMESTAMP public Date modified;
      ....
    }
    

    这些单行定义注释:

    @Retention(RUNTIME) @Target(FIELD) public @interface CreatedDate {}
    @Retention(RUNTIME) @Target(FIELD) public @interface ModifiedDate {}
    

    您可以将它们放在自己的文件中,也可以将它们放在一些现有的类中。我更喜欢前者,所以我得到了一个更清洁的完全合格的名字。

    这是Entity监听器类:

    public class Traceability
    {
      private final ConcurrentMap<Class<?>, TracedFields> fieldCache = new ConcurrentHashMap<>();
    
      @PrePersist
      public void prePersist(Object o) { touchFields(o, true); }
    
      @PreUpdate
      public void preUpdate(Object o) { touchFields(o, false); }
    
      private void touchFields(Object o, boolean creation) {
        final Date now = new Date();
        final Consumer<? super Field> touch = f -> uncheckRun(() -> f.set(o, now));
        final TracedFields tf = resolveFields(o);
        if (creation) tf.created.ifPresent(touch);
        tf.modified.ifPresent(touch);
      }
    
      private TracedFields resolveFields(Object o) {
        return fieldCache.computeIfAbsent(o.getClass(), c -> {
          Field created = null, modified = null;
          for (Field f : c.getFields()) {
            if (f.isAnnotationPresent(CreatedDate.class)) created = f;
            else if (f.isAnnotationPresent(ModifiedDate.class)) modified = f;
            if (created != null && modified != null) break;
          }
          return new TracedFields(created, modified);
        });
      }
    
      private static class TracedFields {
        public final Optional<Field> created, modified;
    
        public TracedFields(Field created, Field modified) {
          this.created = Optional.ofNullable(created);
          this.modified = Optional.ofNullable(modified);
        }
      }
    
      // Java's ill-conceived checked exceptions are even worse when combined with
      // lambdas. Below is what we need to achieve "exception transparency" (ability
      // to let checked exceptions escape the lambda function). This disables
      // compiler's checking of exceptions thrown from the lambda, so it should be 
      // handled with utmost care.      
    
      public static void uncheckRun(RunnableExc r) {
        try { r.run(); }
        catch (Exception e) { sneakyThrow(e); }
      }
    
      public interface RunnableExc { void run() throws Exception; }
    
      public static <T> T sneakyThrow(Throwable e) { 
        return Traceability.<RuntimeException, T> sneakyThrow0(e); 
      }
    
      @SuppressWarnings("unchecked") 
      private static <E extends Throwable, T> T sneakyThrow0(Throwable t) throws E {
        throw (E) t;
      }
    }
    

    最后,如果您不使用JPA但使用经典的Hibernate,则需要激活其JPA事件模型集成。这很简单,只需确保类路径包含文件META-INF/services/org.hibernate.integrator.spi.Integrator,其内容中包含以下单行:

    org.hibernate.jpa.event.spi.JpaIntegrator
    

    通常,对于Maven项目,您只需将其放在src/main/resources目录下。

答案 5 :(得分:2)

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

.
.
.

@CreationTimestamp
private Date created;

@UpdateTimestamp
private Date modified;

答案 6 :(得分:1)

由于在使用Hibernate Session时忽略了@PrePersist和@PreUpdate,我使用拦截器做了一个相对简单的解决方案:

  1. 定义“可审核”界面:

    public interface Auditable {
      public void setUpdated_at(Date date);
      public void setCreated_at(Date date);
    }
    
  2. 定义一个类“AuditableInterceptor”

    public class AuditableInterceptor extends EmptyInterceptor {
    
        private static final long serialVersionUID = -3557239720502671471L;
    
        @override
        public boolean onFlushDirty(Object entity,
                Serializable id,
                Object[] currentState,
                Object[] previousState,
                String[] propertyNames,
                Type[] types) {
    
            if (entity instanceof Auditable) {
                for (int i = 0; i < propertyNames.length; i++) {
                    if ("updated_at".equals(propertyNames[i])) {
                        currentState[i] = new Date();
                        return true;
                    }
                }
            }
            return false;
        }
    
        @override
        public boolean onSave(Object entity,
                Serializable id,
                Object[] state,
                String[] propertyNames,
                Type[] types) {
    
            if (entity instanceof Auditable) {
                for (int i = 0; i < propertyNames.length; i++) {
                    if ("created_at".equals(propertyNames[i])) {
                        state[i] = new Date();
                        return true;
                    }
                }
            }
            return false;
        }
    }
    
  3. 在打开新会话时指定拦截器(您可能在实用程序类中有此功能)

    sessionFactory.openSession(new AuditableInterceptor());
    // sessionFactory.openSession();
    
  4. 在您的实体中实施Auditable接口,例如

    @Entity
    public class Product implements Auditable {
    
        ...
        private Date created_at;
        private Date updated_at;
        ...
    
        public Product() {
        }
    
        ...
    
        @Temporal(javax.persistence.TemporalType.TIMESTAMP)
        public Date getCreated_at() {
            return created_at;
        }
    
        public void setCreated_at(Date created_at) {
            this.created_at = created_at;
        }
    
        @Temporal(javax.persistence.TemporalType.TIMESTAMP)
        public Date getUpdated_at() {
            return updated_at;
        }            @Override
    
        @Override
        public void setUpdated_at(Date updated_at) {
            this.updated_at = updated_at;
        }
        ...
    }
    
  5. 注意:

    1. 此示例需要属性created_at和updated_at。对于不同的名称,方法名称也必须进行调整。
    2. 类型必须是org.hibernate.type.Type!

答案 7 :(得分:0)

最近我遇到了同样的问题,使用hibernate sessionFactory时,JPA-Annotations @PrePersist@PreUpdate将无效。

使用Hibernate 5的一个简单方法是将字段声明为@Version,每次更新数据库实例时都会正确更新实体的时间戳/ localDateTime。