Festlegen einer JPA-Zeitstempelspalte, die von der Datenbank generiert werden soll?

70

In meiner SQL Server 2000 - Datenbank, habe ich einen Zeitstempel (in Funktion nicht in Datentyp) Spalt vom Typ DATETIMEnamens lastTouchedSatz getdate()als den Standardwert / bindend.

Ich verwende die von Netbeans 6.5 generierten JPA-Entitätsklassen und habe diese in meinem Code

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

Wenn ich jedoch versuche, das Objekt in die Datenbank zu stellen, erhalte ich Folgendes:

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

Ich habe versucht, das auf @Basiczu setzen (optional = true), aber das löst eine Ausnahme aus, die besagt, dass die Datenbank keine nullWerte für die TIMESTAMPSpalte zulässt , was nicht beabsichtigt ist.

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

Ich habe dies zuvor im reinen Ruhezustand zum Laufen gebracht, bin aber inzwischen auf JPA umgestiegen und habe keine Ahnung, wie ich sagen soll, dass diese Spalte auf der Datenbankseite generiert werden soll. Beachten Sie, dass ich immer noch den Ruhezustand als JPA-Persistenzschicht verwende.

James McMahon
quelle

Antworten:

58

Ich habe das Problem behoben, indem ich den Code in geändert habe

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

Daher wird die Zeitstempelspalte beim Generieren von SQL-Einfügungen ignoriert. Ich bin mir nicht sicher, ob dies der beste Weg ist. Feedback ist willkommen.

James McMahon
quelle
Wenn ich mehr darüber nachdenke, denke ich, dass ich die Spalte beim Aktualisieren benötigen werde, da ich sie über Java aktualisieren muss, wenn ich Spaltenänderungen vornehme. Es sei denn, es gibt eine Möglichkeit, die Spaltenaktualisierung auf der Datenbankseite durchzuführen.
James McMahon
1
Laut stackoverflow.com/questions/36001/… sollte ich hier einen Trigger verwenden.
James McMahon
Was ist mit anderen Typen wie LocalDateTime? Mit dieser Lösung bekomme ichError creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @Temporal should only be set on a java.util.Date or java.util.Calendar
Emecas
46

Mir ist klar, dass dies etwas spät ist, aber ich hatte Erfolg damit, eine Zeitstempelspalte mit zu kommentieren

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

Dies sollte auch mit CURRENT_DATEund funktionieren CURRENT_TIME. Ich verwende JPA / Hibernate mit Oracle, also YMMV.

Matt Luongo
quelle
7
Das hat bei uns funktioniert! Wir haben es kombiniert mitinsertable=false, updatable=false
Shervin Asgari
ERROR 4371 --- [ main] org.hibernate.tool.hbm2ddl.SchemaExport : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP` nicht null, full_contentLangtext nicht null 'in Zeile 1 . My column is: @Column (nullable = false, name = "created_at", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP") `undprivate Timestamp createdAt;
e-info128
1
Dies ist eine kurze und süße Lösung. Danke
Vimukthi Sineth
15
@Column(nullable = false, updatable = false)
@CreationTimestamp
private Date created_at;

das hat bei mir funktioniert. Mehr Info

maximus
quelle
7

Fügen Sie die @CreationTimestampAnmerkung hinzu:

@CreationTimestamp
@Column(name="timestamp", nullable = false, updatable = false, insertable = false)
private Timestamp timestamp;
Dhwanil Patel
quelle
4

Ich habe dies gut mit JPA2.0 und MySQL 5.5.10, für Fälle, in denen es mir nur darum geht, wann die Zeile das letzte Mal geändert wurde. MySQL erstellt beim ersten Einfügen einen Zeitstempel und jedes Mal, wenn UPDATE in der Zeile aufgerufen wird. (HINWEIS: Dies ist problematisch, wenn es mich interessiert, ob das UPDATE tatsächlich eine Änderung vorgenommen hat oder nicht).

Die Spalte "Zeitstempel" in diesem Beispiel ähnelt einer Spalte "zuletzt berührt" .x`

Der folgende Code verwendet eine separate Spalte "Version" für optimistisches Sperren.

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

(HINWEIS: @Version funktioniert nicht in einer MySQL-Spalte "DATETIME", in der der Attributtyp "Date" in der Entity-Klasse lautet. Dies lag daran, dass Date einen Wert bis zur Millisekunde generiert hat, MySQL jedoch die Millisekunde nicht gespeichert hat Als also ein Vergleich zwischen dem Inhalt der Datenbank und der "angehängten" Entität durchgeführt wurde, dachte man, sie hätten unterschiedliche Versionsnummern.

Aus dem MySQL-Handbuch zu TIMESTAMP :

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

Ich glaube nicht, dass jede Datenbank Zeitstempel für die automatische Aktualisierung hat (z. B. Postgres). Daher habe ich beschlossen, dieses Feld überall in meinem Code manuell zu aktualisieren. Dies funktioniert mit jeder Datenbank:

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

Es gibt Gründe, Trigger zu verwenden, aber für die meisten Projekte ist dies keiner von ihnen. Trigger führen Sie noch tiefer in eine bestimmte Datenbankimplementierung ein.

MySQL 5.6.28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66) scheint sehr nachsichtig zu sein und erfordert nichts weiter

@Column(name="LastTouched")

MySQL 5.7 .9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72) funktioniert nur mit

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

nicht:

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

Meine anderen Systeminformationen (in beiden Umgebungen identisch)

  • hibernate-entitymanager 5.0.2
  • Hibernate-Validator 5.2.2
  • mysql-connector-java 5.1.38
GlenPeterson
quelle
Sehr hilfreiche Infos, danke - ich kann mir vorstellen, dass viele Leute von diesem Upgrade auf MySQL 5.7.x gebissen werden
James
@ James - danke, aber ich habe nur die Unterschiede aufgelistet, von denen ich wusste. Ich weiß nicht genau, was der entscheidende Unterschied ist. Es könnte etwas ganz anderes sein. Hoffentlich kann jemand anderes auf dieser Antwort aufbauen.
GlenPeterson
1
Ja, tut mir leid, ich hätte hinzufügen sollen, dass ich diesen Fehler nach dem Upgrade von MySQL 5.6 auf 5.7 unter Ubuntu 14.04.1 festgestellt habe (Betriebssystem und JDK wurden AFAIK nicht geändert). Ich ging also davon aus, dass die MySQL-Version hier der entscheidende Faktor ist.
James
1
@ James - wow, das ist wirklich nützlich. Vielen Dank! Ich habe meine Antwort aktualisiert, um dies widerzuspiegeln.
GlenPeterson
3

Wenn Sie Ihre Entität mit @DynamicInsertz

@Entity
@DynamicInsert
@Table(name = "TABLE_NAME")
public class ClassName implements Serializable  {

Im Ruhezustand wird SQL ohne nullWerte generiert . Dann fügt die Datenbank ihren eigenen Standardwert ein. Dies hat Auswirkungen auf die Leistung. Siehe [Dynamisches Einfügen] [1].

ponder275
quelle
3

Ich poste dies für Leute, die nach einer Antwort suchen, wenn sie MySQL und Java Spring Boot JPA verwenden, wie @immanuelRocha sagt, nur @CreationTimeStamp auf die @ Spalte im Frühjahr setzen und in MySQL den Standardwert auf "CURRENT_TIMESTAMP" setzen.

Fügen Sie im Frühjahr nur die Zeile hinzu:

@Column(name = "insert_date")
@CreationTimestamp
private Timestamp insert_date;

Alessandro Tempo
quelle
2

Das funktioniert auch bei mir: -

@Temporal(TemporalType.TIMESTAMP)   
@Column(name = "CREATE_DATE_TIME", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
public Date getCreateDateTime() {
    return createDateTime;
}

public void setCreateDateTime(Date createDateTime) {
    this.createDateTime = createDateTime;
}
Mandeep Singh Gill
quelle
1

Wenn Sie in Java 8 und Hibernate 5 oder Spring Boot JPA entwickeln, verwenden Sie die folgenden Anmerkungen direkt in Ihrer Entity-Klasse. Der Ruhezustand ruft den aktuellen Zeitstempel von der VM ab und fügt Datum und Uhrzeit in die Datenbank ein.

public class YourEntity {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
    @CreationTimestamp
    private LocalDateTime createdDateTime;
 
    @UpdateTimestamp
    private LocalDateTime updatedDateTime;
 
    …
}
anandchaugule
quelle
0
@Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
@Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`
immanuelRocha
quelle
-1

Das hat bei mir funktioniert:

@Column(name = "transactionCreatedDate", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
Nithin Poosarla
quelle