java - value - Timestamp de criação e registro de data e hora da última atualização com o Hibernate e o MySQL




spring timestamp annotation (9)

Para uma certa entidade do Hibernate, temos um requisito para armazenar seu tempo de criação e a última vez que foi atualizado. Como você projetaria isso?

  • Quais tipos de dados você usaria no banco de dados (assumindo o MySQL, possivelmente em um fuso horário diferente do que a JVM)? Os tipos de dados terão reconhecimento de fuso horário?

  • Quais tipos de dados você usaria em Java ( Date , Calendar , long , ...)?

  • Quem você faria responsável por definir os timestamps - o banco de dados, o framework ORM (Hibernate) ou o programador de aplicativos?

  • Quais anotações você usaria para o mapeamento (por exemplo, @Temporal )?

Não estou apenas procurando por uma solução funcional, mas por uma solução segura e bem projetada.

https://code.i-harness.com



Apenas para reforçar: java.util.Calender não é para Timestamps . java.util.Date é por um momento, agnóstico de coisas regionais como fusos horários. A maioria dos bancos de dados armazena coisas dessa maneira (mesmo que pareçam não fazer isso; geralmente é uma configuração de fuso horário no software cliente; os dados são bons)


Com a solução de Olivier, durante as declarações de atualização você pode encontrar:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: a coluna 'created' não pode ser nula

Para resolver isso, adicione updatable = false à anotação @Column do atributo "created":

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

Como tipo de dados no JAVA, eu recomendo fortemente usar o java.util.Date. Eu encontrei problemas de fuso horário bastante desagradáveis ​​ao usar o Google Agenda. Veja este Thread .

Para definir os timestamps, eu recomendaria usar uma abordagem AOP ou você poderia simplesmente usar Triggers na tabela (na verdade, essa é a única coisa que eu acho que o uso de triggers é aceitável).


Obrigado a todos que ajudaram. Depois de fazer uma pesquisa eu mesmo (eu sou o cara que fez a pergunta), aqui está o que eu encontrei para fazer mais sentido:

  • Tipo de coluna de banco de dados: o número de milissegundos agnóstico de fuso horário desde 1970 representado como decimal(20) porque 2 ^ 64 tem 20 dígitos e o espaço em disco é barato; vamos ser direto. Além disso, não usarei nem o DEFAULT CURRENT_TIMESTAMP nem os triggers. Eu não quero mágica no DB.

  • Tipo de campo Java: long . O timestamp do Unix é bem suportado em várias libs, não tem problemas com o Y2038, a aritmética do timestamp é rápida e fácil (principalmente operator < e operator + , assumindo que nenhum dia / mês / ano esteja envolvido nos cálculos). E, mais importante, tanto os primitivos s long quanto os java.lang.Long s são imutáveis ​​- efetivamente passados ​​por valor - ao contrário de java.util.Date s; Eu ficaria muito chateado para encontrar algo como foo.getLastUpdate().setTime(System.currentTimeMillis()) ao depurar o código de outra pessoa.

  • O framework ORM deve ser responsável por preencher os dados automaticamente.

  • Eu não testei isso ainda, mas apenas olhando para os documentos eu assumo que o @Temporal fará o trabalho; Não tenho certeza se devo usar o @Version para esse propósito. @PrePersist e @PreUpdate são boas alternativas para controlar isso manualmente. Adicionar isso ao supertipo de camada (classe base comum) para todas as entidades é uma idéia interessante, desde que você realmente deseje o registro de data e hora para todas as suas entidades.


Se você estiver usando as anotações JPA, poderá usar os ganchos de evento @PreUpdate e @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();
  }
}

ou você pode usar a anotação @EntityListener na classe e colocar o código do evento em uma classe externa.


Uma boa abordagem é ter uma classe base comum para todas as suas entidades. Nesta classe base, você pode ter sua propriedade id se ela for comumente nomeada em todas as suas propriedades de entidades (um design comum), sua criação e a última data de atualização.

Para a data de criação, você simplesmente mantém uma propriedade java.util.Date . Certifique-se de sempre inicializá-lo com a nova data () .

Para o último campo de atualização, você pode usar uma propriedade de carimbo de data / hora, você precisa mapeá-lo com @Version. Com esta anotação, a propriedade será atualizada automaticamente pelo Hibernate. Esteja ciente de que o Hibernate também aplicará bloqueio otimista (é uma coisa boa).


Você pode apenas usar @CreationTimestamp e @UpdateTimestamp :

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

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;

Você também pode usar um interceptor para definir os valores

Crie uma interface chamada TimeStamped que suas entidades implementam

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

Definir o interceptor

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

E registre-o na fábrica da sessão





timestamp