How to autogenerate created or modified timestamp field?

后端 未结 8 1379
独厮守ぢ
独厮守ぢ 2020-12-05 03:11

My entity class:

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

    @Id
           


        
相关标签:
8条回答
  • 2020-12-05 03:27

    In 4.3 Hibernate with JPA, one can use "@CreationTimestamp" and "@UpdateTimestamp" directly in the date fields

    CreationTimestamp java doc

    UpdateTimestamp java doc

    0 讨论(0)
  • 2020-12-05 03:29
    import org.hibernate.annotations.CreationTimestamp;
    import org.hibernate.annotations.UpdateTimestamp;
    
    .
    .
    .
    
    @CreationTimestamp
    private Date created;
    
    @UpdateTimestamp
    private Date modified;
    
    0 讨论(0)
  • 2020-12-05 03:30

    You can just create a new Date() whenever your instance is created, and then update the updated field whenever the entity gets updated:

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

    Don't provide a setter for any of these methods, only getters.

    0 讨论(0)
  • 2020-12-05 03:31

    Since @PrePersist and @PreUpdate are ignored when Hibernate Session is used I've made a relatively simple solution using Interceptors:

    1. Define an interface "Auditable":

      public interface Auditable {
        public void setUpdated_at(Date date);
        public void setCreated_at(Date date);
      }
      
    2. Define a class "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. Specify the Interceptor when ever you open a new session (you'll likely have this in a utility class)

      sessionFactory.openSession(new AuditableInterceptor());
      // sessionFactory.openSession();
      
    4. Implement the Auditable interface in your entities, e.g.

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

    Notes:

    1. This example expects the properties created_at and updated_at. For different names the method names have also to be adapted.
    2. Type must be org.hibernate.type.Type!
    0 讨论(0)
  • 2020-12-05 03:32

    You can go with Spring Data JPA, Spring has made it as easy using annotation @CreatedBy, @CreatedDate, @LastModifiedBy, @LastModifiedDate on your fields. You can follow below simple example

    // 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
    } 
    

    You can read more on my article Spring Data JPA Auditing: Saving CreatedBy, CreatedDate, LastModifiedBy, LastModifiedDate automatically for more details.

    0 讨论(0)
  • 2020-12-05 03:36

    Since this is a common problem, and there are a lot of half-bred solutions reachable by searching, let me present what I settled on:

    1. define two trivial field annotations, @CreatedDate and @ModifiedDate;
    2. use them to annotate the corresponding fields on your entity;
    3. register the entity class with the Traceability listener, presented below.

    This is how your entity class would look:

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

    These one-liners define the annotations:

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

    You can put them in their own files, or you can bunch them up inside some existing class. I prefer the former so I get a cleaner fully-qualified name for them.

    Here's the Entity listener class:

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

    Finally, if you aren't working with JPA but with classic Hibernate, you need to activate its JPA event model integration. This is very simple, just make sure that the classpath contains the file META-INF/services/org.hibernate.integrator.spi.Integrator, with the following single line in its contents:

    org.hibernate.jpa.event.spi.JpaIntegrator
    

    Typically for a Maven project, you just need to put this under your src/main/resources directory.

    0 讨论(0)
提交回复
热议问题