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

90
在我的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),但这会抛出一个异常,说数据库不允许null值用于TIMESTAMP列,这是按设计来的。

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持久层。

12个回答

72

我通过修改代码解决了问题:

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

因此,在生成SQL插入语句时,时间戳列将被忽略。不确定这是否是最佳方法。欢迎反馈。


其实,我再想想,我觉得我需要在更新时使用该列,因为当我进行列更改时,我需要通过Java进行更新。除非有一种方法可以在数据库端进行列更新。 - James McMahon
2
根据https://dev59.com/mXVD5IYBdhLWcg3wQZUg,我应该在这里使用触发器。 - James McMahon
其他类型如 LocalDateTime 呢?使用这个解决方案,我得到了“在类路径资源 [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class] 中定义的名为 'entityManagerFactory' 的 bean 创建错误:初始化方法调用失败;嵌套异常是 org.hibernate.AnnotationException: @Temporal 应该仅设置在 java.util.Date 或 java.util.Calendar 上”的错误信息。 - emecas

58

我知道有点晚了,但我已经成功地使用时间戳列进行了注释

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

这也适用于 CURRENT_DATECURRENT_TIME。我正在使用带有Oracle的JPA/Hibernate,所以可能与你的情况略有不同。


8
这对我们很有效!我们将其与insertable=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_TIMESTAMPnot null,full_content longtext not null' at line 1. My column is: @Column(nullable = false, name="created_at", updatable = false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP") and private Timestamp createdAt; - e-info128
1
这是一个简洁而精炼的解决方案。谢谢。 - Vimukthi Sineth

29
@Column(nullable = false, updatable = false)
@CreationTimestamp
private Date created_at;

这适用于我。 更多信息


2
属性值将被设置为当前VM日期。这可能是数据库系统设置的时间,也可能不是,这取决于它们的时钟是否同步...在大多数情况下应该没问题,除非同步多个系统至关重要。 - ntg

15

添加@CreationTimestamp注释:

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

1
属性值将被设置为当前VM日期。这可能是数据库系统设置的时间,也可能不是,这取决于它们的时钟是否同步...在大多数情况下应该没问题,除非同步多个系统至关重要。 - ntg

12
如果你正在使用Java 8和Hibernate 5或Spring Boot JPA进行开发,则可以在实体类中直接使用以下注解。Hibernate会从VM获取当前时间戳,并将日期和时间插入到数据库中。请使用下列注解
public class YourEntity {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
    @CreationTimestamp
    private LocalDateTime createdDateTime;
 
    @UpdateTimestamp
    private LocalDateTime updatedDateTime;
 
    …
}

3
属性值将被设置为当前VM日期。这可能是数据库系统设置的时间,也可能不是,这取决于它们的时钟是否同步...在大多数情况下应该没问题,除非同步多个系统至关重要。 - ntg

6

我认为并不是每个数据库都有自动更新时间戳的功能(例如Postgres)。因此,我决定在我的代码中手动更新此字段。这将适用于任何数据库:

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

使用触发器的原因是有的,但对于大多数项目来说,这并不是其中之一。触发器会让你更深入地了解特定数据库的实现。

MySQL 5.6.28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66)似乎非常宽容,不需要除此以外的任何东西。

@Column(name="LastTouched")

MySQL 5.7.9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72)只能与以下版本一起使用:

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

请注意:

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

我的另一个系统信息(在两个环境中都相同)

  • hibernate-entitymanager 5.0.2
  • hibernate-validator 5.2.2
  • mysql-connector-java 5.1.38

以上是关于IT技术的内容,涉及到Hibernate和MySQL的版本信息。

非常有用的信息,谢谢 - 我想很多人在升级到MySQL 5.7.x时会遇到这个问题。 - James
@James - 谢谢,但我只列出了我知道的差异。我不确定关键的区别是什么。它完全可能是其他东西。希望有人能在这个答案的基础上进一步发展。 - GlenPeterson
1
是的,抱歉我应该补充一下,在Ubuntu 14.04.1上从MySQL 5.6升级到5.7后遇到了这个错误(据我所知,操作系统和JDK没有更改)。因此,我认为MySQL版本是关键因素。 - James
1
@James - 哇,这真的很有用。谢谢!我已经更新了我的答案以反映这一点。 - GlenPeterson

5
我已经成功使用JPA2.0和MySQL 5.5.10进行了编程,用于关注行最后一次修改时间的情况。MySQL会在第一次插入时创建一个时间戳,并在每次对该行调用UPDATE时更新时间戳。(注意:如果我关心UPDATE是否实际上有所改变,这将成为问题)。
在本例中,“时间戳”列类似于“最后操作时间”列。
以下代码使用单独的“版本”列进行乐观锁定。
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;
}

注意:@Version在MySQL的“DATETIME”列上不起作用,其中实体类中的属性类型为“Date”。这是因为Date生成了一个毫秒级的值,但MySQL没有存储毫秒级别的值,所以当它在数据库中比较与“附加”的实体之间的差异时,它认为它们具有不同的版本号。

关于TIMESTAMP的MySQL手册

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

5
如果您使用@DynamicInsert标记实体,例如:
@Entity
@DynamicInsert
@Table(name = "TABLE_NAME")
public class ClassName implements Serializable  {

Hibernate会在生成SQL时省略null值, 然后数据库会插入自己的默认值。这会对性能产生影响,详情请参见[Dynamic Insert][1]。

4
我将为使用MySQL和Java Spring Boot JPA的人们提供翻译,如@immanuelRocha所说,在Spring中只需要在@Column后加入@CreationTimeStamp,并在MySQL中将默认值设置为"CURRENT_TIMESTAMP"。

在Spring中,只需添加以下代码:

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


属性值将被设置为当前VM日期。这可能是数据库系统设置的时间,也可能不是,这取决于它们的时钟是否同步...在大多数情况下应该没问题,除非同步多个系统至关重要。 - ntg
1
在大多数情况下,这个解决方案是正确的,除非用户需要另一个时钟时间。 - Alessandro Pace

3
这对我也有效:-
@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;
}

假设本地时钟不同步,我认为这将添加本地系统的时间,而不是数据库系统的时间。 - ntg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接