设置由数据库生成的 JPA 时间戳列?

在我的 SQL Server 2000数据库中,有一个时间戳(函数类型不是数据类型)类型为 DATETIME的列,名为 lastTouched,设置为 getdate()作为其默认值/绑定。

我正在使用 Netbeans 6.5生成的 JPA 实体类,并且在我的代码中有这个类

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

但是,当我试图将对象放入数据库时,

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

我已经尝试将 @Basic设置为 (optional = true),但是这抛出了一个异常,即数据库不允许 TIMESTAMP列使用 null值,这是设计上不允许的。

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

我以前在纯 Hibernate 中使用过这种方法,但后来我转到了 JPA,不知道如何告诉它这个列应该是在数据库端生成的。注意,我仍然使用 Hibernate 作为我的 JPA 持久层。

244387 次浏览

I fixed the issue by changing the code to

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

So the timestamp column is ignored when generating SQL inserts. Not sure if this is the best way to go about this. Feedback is welcome.

I realize this is a bit late, but I've had success with annotating a timestamp column with

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

This should also work with CURRENT_DATE and CURRENT_TIME. I'm using JPA/Hibernate with Oracle, so YMMV.

I have this working well using JPA2.0 and MySQL 5.5.10, for cases where I only care about the last time the row was modified. MySQL will create a timestamp on first insertion, and every time UPDATE is called on the row. (NOTE: this will be problematic if I cared whether or not the UPDATE actually made a change).

The "timestamp" column in this example is like a "last-touched" column.x`

The code below uses a separate column "version" for optimistic locking.

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

(NOTE: @Version doesn't work on a MySQL "DATETIME" column, where the attribute type is "Date" in the Entity class. This was because Date was generating a value down to the millisecond, however MySQL was not storing the millisecond, so when it did a comparison between what was in the database, and the "attached" entity, it thought they had different version numbers)

From the MySQL manual regarding TIMESTAMP :

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

I do not think that every database has auto-update timestamps (e.g. Postgres). So I've decided to update this field manually everywhere in my code. This will work with every database:

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

There are reasons to use triggers, but for most projects, this is not one of them. Triggers dig you even deeper into a specific database implementation.

MySQL 5.6.28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66) seems to be very forgiving, not requiring anything beyond

@Column(name="LastTouched")

MySQL 5.7.9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72) only works with

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

not:

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

My other system info (identical in both environments)

  • hibernate-entitymanager 5.0.2
  • hibernate-validator 5.2.2
  • mysql-connector-java 5.1.38
@Column(nullable = false, updatable = false)
@CreationTimestamp
private Date created_at;

this worked for me. more info

@Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
@Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`

This worked for me:

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

Add the @CreationTimestamp annotation:

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

If you mark your entity with @DynamicInsert e.g.

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

Hibernate will generate SQL without null values. Then the database will insert its own default value. This does have performance implications See [Dynamic Insert][1].

This also works for me:-

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

I'm posting this for people searching for an answer when using MySQL and Java Spring Boot JPA, like @immanuelRocha says, only have too @CreationTimeStamp to the @Column in Spring, and in MySQL set the default value to "CURRENT_TIMESTAMP".

In Spring add just the line :

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

If you are doing development in Java 8 and Hibernate 5 Or Spring Boot JPA then use following annotation directly in your Entity class. Hibernate gets the current timestamp from the VM and will insert date and time in database.

public class YourEntity {
 

@Id
@GeneratedValue
private Long id;
 

private String name;
 

@CreationTimestamp
private LocalDateTime createdDateTime;
 

@UpdateTimestamp
private LocalDateTime updatedDateTime;
 

…
}