Как автоматически создать созданное или измененное поле метки времени? - PullRequest
14 голосов
/ 10 марта 2011

Мой класс сущности:

@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 ]

34 голосов
/ 17 февраля 2016

В 4.3 Hibernate с JPA можно использовать «@CreationTimestamp» и «@UpdateTimestamp» непосредственно в полях даты

CreationTimestamp java doc

UpdateTimestamp Java документ

18 голосов
/ 10 марта 2011

Вы можете просто создать new Date() всякий раз, когда создается ваш экземпляр, а затем обновлять поле updated при каждом обновлении сущности:

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

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

Не предоставляйте установщик для любого из этихметоды, только добытчики.

13 голосов
/ 10 марта 2011

Мы делаем это с 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)
12 голосов
/ 10 февраля 2017

Вы можете использовать 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: сохранение CreatedBy, CreatedDate, LastModifiedBy, LastModifiedDate автоматически для получения более подробной информации.

2 голосов
/ 11 января 2017
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

.
.
.

@CreationTimestamp
private Date created;

@UpdateTimestamp
private Date modified;
2 голосов
/ 18 ноября 2014

Поскольку это распространенная проблема, и есть много полукровных решений, доступных с помощью поиска, позвольте мне представить, на чем я остановился:

  1. определяют две тривиальные аннотации полей, @CreatedDateи @ModifiedDate;
  2. используйте их для аннотирования соответствующих полей в вашей сущности;
  3. зарегистрируйте класс сущности с помощью прослушивателя Traceability, представленного ниже.

Вот как будет выглядеть ваш класс сущности:

@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.Это очень просто, просто убедитесь, что classpath содержит файл META-INF/services/org.hibernate.integrator.spi.Integrator со следующей строкой в ​​его содержании:

org.hibernate.jpa.event.spi.JpaIntegrator

Как правило, для проекта Maven, вам просто нужно поместить это подsrc/main/resources каталог.

1 голос
/ 22 января 2015

Поскольку @PrePersist и @PreUpdate игнорируются при использовании Hibernate Session, я сделал относительно простое решение с использованием перехватчиков:

  1. Определение интерфейса "Auditable":

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

Примечания:

  1. В этом примере ожидаются свойства create_at и updated_at. Для разных имен также должны быть адаптированы имена методов.
  2. Тип должен быть org.hibernate.type.Type!
0 голосов
/ 13 января 2017

Недавно я столкнулся с той же проблемой, и JPA-аннотации @PrePersist и @PreUpdate не будут работать при использовании hibernate sessionFactory.

Простой способ, который работал для меня с Hibernate 5, - это объявитьполе как @Version, которое будет корректно обновлять timestamp / localDateTime объекта при каждом обновлении экземпляра базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...