88

In my SQL Server 2000 database, I have a timestamp (in function not in data type) column of type DATETIME named lastTouched set to getdate() as its default value/binding.

I am using the Netbeans 6.5 generated JPA entity classes, and have this in my code

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

However when I try to put the object into the database I get,

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

I've tried setting the @Basic to (optional = true), but that throws an exception saying the database doesn't allow null values for the TIMESTAMP column, which it doesn't by design.

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

I previously got this to work in pure Hibernate, but I have since switched over to JPA and have no idea how to tell it that this column is supposed to be generated on the database side. Note that I am still using Hibernate as my JPA persistence layer.

Mohit Gupta
  • 33
  • 1
  • 5
James McMahon
  • 48,506
  • 64
  • 207
  • 283

12 Answers12

71

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.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
  • Actually, thinking about it more, I think I will need the column on update, as I am going to need to update it through Java when I make column changes. Unless there is a way to do the column update on the database side somehow. – James McMahon May 06 '09 at 15:18
  • 2
    According to http://stackoverflow.com/questions/36001/sql-server-2005-auto-updated-datetime-column-lastupdated, a trigger is what I should be using here. – James McMahon May 06 '09 at 15:53
  • what about for other types like LocalDateTime ? WIth this solution I am getting `Error 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 Oct 02 '20 at 16:04
58

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.

GlenPeterson
  • 4,866
  • 5
  • 41
  • 49
Matt Luongo
  • 14,371
  • 6
  • 53
  • 64
  • 8
    This worked for us! We combined it with `insertable=false, updatable=false` – Shervin Asgari Apr 07 '16 at 11:46
  • `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` not 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 Sep 23 '17 at 01:02
  • 1
    This is a short and sweet solution. Thanks – Vimukthi Sineth Jan 20 '20 at 03:34
29
@Column(nullable = false, updatable = false)
@CreationTimestamp
private Date created_at;

this worked for me. more info

mchouhan_google
  • 1,775
  • 1
  • 20
  • 28
  • 2
    https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/CreationTimestamp.html : The property value will be set to the current VM date. This might or not be the time the database system would set depending if their clocks are on sync... Should be ok in most cases, unless synchronizing multiple systems is critical. – ntg Jun 10 '21 at 09:25
15

Add the @CreationTimestamp annotation:

@CreationTimestamp
@Column(name="timestamp", nullable = false, updatable = false, insertable = false)
private Timestamp timestamp;
Justin Albano
  • 3,809
  • 2
  • 24
  • 51
Dhwanil Patel
  • 2,273
  • 1
  • 18
  • 28
  • 1
    https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/CreationTimestamp.html : The property value will be set to the current VM date. This might or not be the time the database system would set depending if their clocks are on sync... Should be ok in most cases, unless synchronizing multiple systems is critical. – ntg Jun 10 '21 at 09:23
11

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;
 
    …
}
anandchaugule
  • 901
  • 12
  • 20
  • 3
    https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/CreationTimestamp.html : The property value will be set to the current VM date. This might or not be the time the database system would set depending if their clocks are on sync... Should be ok in most cases, unless synchronizing multiple systems is critical. – ntg Jun 10 '21 at 09:23
6

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
GlenPeterson
  • 4,866
  • 5
  • 41
  • 49
  • Very helpful info, thanks - I imagine a lot of people will get bitten by this upgrading to MySQL 5.7.x – James Feb 15 '16 at 22:57
  • @James - thanks, but I just listed the differences I knew about. I don't know for sure what the critical difference is. It could be something else entirely. Hopefully someone else can build on this answer. – GlenPeterson Feb 16 '16 at 22:38
  • 1
    Yes sorry I should have added that I encountered this error after upgrading from MySQL 5.6 to 5.7 on Ubuntu 14.04.1 (OS and JDK were not changed AFAIK). So I was assuming that the MySQL version was the critical factor here. – James Feb 17 '16 at 10:36
  • 1
    @James - wow, that's really useful. Thanks! I've updated my answer to reflect that. – GlenPeterson Feb 17 '16 at 17:27
5

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.
willtardy
  • 706
  • 2
  • 11
  • 17
5

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].

ponder275
  • 903
  • 2
  • 12
  • 33
4

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;
Alessandro Pace
  • 206
  • 4
  • 8
  • https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/annotations/CreationTimestamp.html : The property value will be set to the current VM date. This might or not be the time the database system would set depending if their clocks are on sync... Should be ok in most cases, unless synchronizing multiple systems is critical. – ntg Jun 10 '21 at 09:24
  • 1
    Correct, in most cases this solution will work, unless the person needs another clock time. – Alessandro Pace Jun 14 '21 at 10:15
3

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;
}
Mandeep Singh Gill
  • 1,145
  • 9
  • 5
  • Suppose the local clock is out of sync, I think this will add the time of the local system, not the database's system. – ntg Jun 10 '21 at 09:16
0
@Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
@Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`
-1

This worked for me:

@Column(name = "transactionCreatedDate", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
RobC
  • 22,977
  • 20
  • 73
  • 80