Отметка времени создания и отметка времени последнего обновления в Hibernate и MySQL - PullRequest
220 голосов
/ 21 октября 2008

Для определенного объекта Hibernate у нас есть требование хранить время его создания и время последнего обновления. Как бы вы спроектировали это?

  • Какие типы данных вы бы использовали в базе данных (предполагая MySQL, возможно, в другом часовом поясе, чем JVM)? Будут ли типы данных учитывать часовой пояс?

  • Какие типы данных вы бы использовали в Java (Date, Calendar, long, ...)?

  • Кого бы вы взяли на себя ответственность за установку меток времени - базы данных, инфраструктуры ORM (Hibernate) или разработчика приложений?

  • Какие аннотации вы бы использовали для сопоставления (например, @Temporal)?

Я ищу не только рабочее решение, но и безопасное и продуманное решение.

Ответы [ 14 ]

250 голосов
/ 21 октября 2008

Если вы используете аннотации JPA, вы можете использовать @PrePersist и @PreUpdate ловушки событий, чтобы сделать это:

@Entity
@Table(name = "entities")    
public class Entity {
  ...

  private Date created;
  private Date updated;

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

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

или вы можете использовать аннотацию @EntityListener для класса и поместить код события во внешний класс.

114 голосов
/ 10 сентября 2016

Вы можете просто использовать @CreationTimestamp и @UpdateTimestamp:

@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date")
private Date createDate;

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
105 голосов
/ 28 октября 2010

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

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 {
...
}
17 голосов
/ 21 октября 2011

Вы также можете использовать перехватчик для установки значений

Создайте интерфейс с именем 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;
    }
}

И зарегистрируйте его на фабрике сессий

13 голосов
/ 25 апреля 2014

С помощью решения Оливье во время операторов обновления вы можете столкнуться с:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: столбец «создан» не может быть пустым

Чтобы решить эту проблему, добавьте Updatable = false к аннотации @Column атрибута «create»:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;
12 голосов
/ 23 октября 2008

Спасибо всем, кто помог. После того, как я провел небольшое исследование сам (я парень, который задал вопрос), вот что я нашел наиболее значимым:

  • Тип столбца базы данных: независимое от часового пояса число миллисекунд с 1970 года, представленное как decimal(20), поскольку 2 ^ 64 имеет 20 цифр, а дисковое пространство дешевое; давайте будем простыми. Также я не буду использовать ни DEFAULT CURRENT_TIMESTAMP, ни триггеры. Я не хочу магии в БД.

  • Тип поля Java: long. Отметка времени Unix хорошо поддерживается различными библиотеками, long не имеет проблем с Y2038, арифметика отметки времени является быстрой и простой (в основном оператор < и оператор +, при условии, что в расчетах не участвуют дни / месяцы / годы). И, что наиболее важно, оба примитива long s и java.lang.Long s неизменны - эффективно передаются по значению & mdash; в отличие от java.util.Date s; Я бы очень разозлился, когда нашел что-то вроде foo.getLastUpdate().setTime(System.currentTimeMillis()) при отладке чужого кода.

  • Платформа ORM должна отвечать за автоматическое заполнение данных.

  • Я еще не проверял это, но только просматривая документы, я предполагаю, что @Temporal выполнит эту работу; не уверен, смогу ли я использовать @Version для этой цели. @PrePersist и @PreUpdate являются хорошими альтернативами для управления этим вручную. Добавление этого в супертип слоя (общий базовый класс) для всех сущностей - это милая идея, при условии, что вы действительно хотите пометить время для всех ваших сущностей.

6 голосов
/ 14 октября 2015

Если вы используете Session API, обратные вызовы PrePersist и PreUpdate не будут работать в соответствии с этим ответом .

Я использую метод persist () Hibernate Session в своем коде, поэтому единственный способ выполнить эту работу - использовать приведенный ниже код и следовать этому сообщению в блоге (также опубликованному в ответе *) 1008 *).

@MappedSuperclass
public abstract class AbstractTimestampEntity {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created")
    private Date created=new Date();

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated")
    @Version
    private Date updated;

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }
}
3 голосов
/ 23 апреля 2018

Теперь есть также аннотации @CreatedDate и @LastModifiedDate.

=> https://programmingmitra.blogspot.fr/2017/02/automatic-spring-data-jpa-auditing-saving-CreatedBy-createddate-lastmodifiedby-lastmodifieddate-automatically.html

(пружинный каркас)

2 голосов
/ 21 октября 2008

Просто для усиления: java.util.Calender не для отметок времени . java.util.Date является на мгновение агностиком региональных вещей, таких как часовые пояса. Большинство баз данных хранят вещи таким образом (даже если они не кажутся; обычно это настройка часового пояса в клиентском программном обеспечении; данные хорошие)

2 голосов
/ 21 октября 2008

Хороший подход - это иметь общий базовый класс для всех ваших сущностей. В этом базовом классе вы можете иметь свойство id, если оно обычно именуется во всех ваших сущностях (общий дизайн), ваших свойствах создания и даты последнего обновления.

На дату создания вы просто сохраняете свойство java.util.Date . Обязательно всегда инициализируйте его с помощью new Date () .

Для последнего поля обновления вы можете использовать свойство Timestamp, вам нужно отобразить его с помощью @Version. С этой аннотацией свойство будет автоматически обновляться Hibernate. Помните, что Hibernate также применяет оптимистическую блокировку (это хорошо).

...