web-dev-qa-db-fra.com

Définition d'une colonne d'horodatage JPA à générer par la base de données?

Dans ma base de données SQL Server 2000, j'ai une colonne timestamp de type DATETIME nommée lastTouched définie sur getdate() comme valeur par défaut/binding .

J'utilise les classes d'entités JPA générées par Netbeans 6.5 et je les ai dans mon code

@Basic(optional = false)
@Column(name = "LastTouched")
@Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;

Cependant, lorsque j'essaie de placer l'objet dans la base de données,

javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.generic.Stuff.lastTouched

J'ai essayé de régler le @Basic à (optional = true), mais cela lève une exception indiquant que la base de données n'autorise pas les valeurs null de la colonne TIMESTAMP, ce qui n'est pas le cas par nature.

ERROR JDBCExceptionReporter - Cannot insert the value NULL into column 'LastTouched', table 'DatabaseName.dbo.Stuff'; column does not allow nulls. INSERT fails.

Auparavant, cela fonctionnait dans Hibernate pur, mais je me suis senti passé à JPA et je ne sais pas comment lui dire que cette colonne est supposée être générée du côté de la base de données. Notez que j'utilise toujours Hibernate comme couche de persistance JPA.

54
James McMahon

J'ai résolu le problème en changeant le code en

@Basic(optional = false)
@Column(name = "LastTouched", insertable = false, updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;

La colonne d'horodatage est donc ignorée lors de la génération d'insertions SQL. Je ne sais pas si c'est la meilleure façon de s'y prendre. Les commentaires sont les bienvenus.

51
James McMahon

Je me rends compte que c’est un peu tard, mais j’ai réussi à annoter une colonne timestamp avec

@Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

Cela devrait également fonctionner avec CURRENT_DATE et CURRENT_TIME. J'utilise JPA/Hibernate avec Oracle, donc YMMV.

36
Matt Luongo
@Column(nullable = false, updatable = false)
@CreationTimestamp
private Date created_at;

cela a fonctionné pour moi. plus d'infos

6
maximus

Cela fonctionne bien avec JPA2.0 et MySQL 5.5.10, dans les cas où je ne me soucie que de la dernière fois que la ligne a été modifiée. MySQL créera un horodatage lors de la première insertion et à chaque appel de UPDATE sur la ligne. (NOTE: cela posera un problème si je voulais savoir si UPDATE avait réellement apporté un changement).

La colonne "timestamp" dans cet exemple est semblable à une colonne "touchée en dernier".

Le code ci-dessous utilise une colonne distincte "version" pour le verrouillage optimiste.

private long version;
private Date timeStamp

@Version
public long getVersion() {
    return version;
}

public void setVersion(long version) {
    this.version = version;
}

// columnDefinition could simply be = "TIMESTAMP", as the other settings are the MySQL default
@Column(name="timeStamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
@Temporal(TemporalType.TIMESTAMP)
public Date getTimeStamp() {
    return timeStamp;
}

public void setTimeStamp(Date timeStamp) {
    this.timeStamp = timeStamp;
}

(REMARQUE: @Version ne fonctionne pas sur une colonne MySQL "DATETIME", où le type d'attribut est "Date" dans la classe Entity. C'est parce que Date générait une valeur à la milliseconde près, mais MySQL ne stockait pas la milliseconde. , donc quand il a comparé le contenu de la base de données avec l'entité "attachée", il a pensé qu'ils avaient des numéros de version différents)

extrait du manuel MySQL concernant TIMESTAMP :

With neither DEFAULT nor ON UPDATE clauses, it is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
4
willtardy

Je ne pense pas que chaque base de données possède des horodatages de mise à jour automatique (par exemple, Postgres). J'ai donc décidé de mettre à jour ce champ manuellement partout dans mon code. Cela fonctionnera avec chaque base de données:

thingy.setLastTouched(new Date());
HibernateUtil.save(thingy);

Il existe des raisons d'utiliser des déclencheurs, mais pour la plupart des projets, ce n'est pas l'un d'entre eux. Déclencheurs Creusez encore plus profondément dans une implémentation de base de données spécifique.

MySQL 5.6. 28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66) semble très tolérant, ne nécessitant rien de plus.

@Column(name="LastTouched")

MySQL 5.7. 9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72) ne fonctionne qu'avec

@Column(name="LastTouched", insertable=false, updatable=false)

ne pas:

FAILED: removing @Temporal
FAILED: @Column(name="LastTouched", nullable=true)
FAILED: @Column(name="LastTouched", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")

Mon autre information système (identique dans les deux environnements)

  • hibernate-entitymanager 5.0.2
  • hibernate-validator 5.2.2
  • mysql-connector-Java 5.1.38
4
GlenPeterson
@Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
@Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`
0
immanuelRocha