I am using an annotation #CreationTimestamp to write a date when a file is uploaded. How can I remove the minutes to leave only the year, month and day?
#Data
#Entity
#Table(name = "exchange_rates")
public class ExchangeRate {
///
#CreationTimestamp
#Column(name = "downloaddate")
private Date downloaddate;
}
You can use #Temporal(TemporalType.DATE) to only store the date part.
Related
I'm using Spring Boot with JPA and Lombok.
My Sample entity contains four dates, the approved date, sample date, and a createdAt and modifiedAt that is maintained by JPA itself with the Auditable class. JPA manages the schema, database running MySQL and all the date columns are DateTime. All Dates are of class java.util.Date.
Sample entity (simplified for reasons)
#Entity
#Data
#EqualsAndHashCode(callSuper = false)
public class Sample extends Auditable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
Date approved;
#DateTimeFormat(pattern = "yyyy-MM-dd'T'H:m")
Date sampleDate;
}
The DateTimeFormat on the sampleDate is to help Spring convert form-data to a java.util.Date.
Auditable.java
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public class Auditable {
#CreatedDate
#Column(name = "created_at", updatable = false, nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
#LastModifiedDate
#Column(name = "modified_at")
#Temporal(TemporalType.TIMESTAMP)
private Date modifiedAt;
}
When my controller performs this:
Sample s = sampleRepository.getOne(id);
s.setApproved(new Date());
sampleRepository.save(s);
Spring Generates this error message:
com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'approved' at row 1
You would better use LocalDateTime instead of java.util.Date or java.sql.Date, if you are doing this with java >= 8.
Audited entities' createdDate, lastModifiedDate should be set by Jpa Auditor(framework), not by client(in this context, you).
Also, you can try #Temporal(TemporalType.DATE) instead of TemporalType.TIMESTAMP if you want to keep your code.
Updated
Sorry about I missed what exact subject you raised.
Just try add #Temporal(TemporalType.DATE) above Date approved;.
I have an entity which is
#AllArgsConstructor
#NoArgsConstructor
#Data
#Entity
#Table(name = "REFRESH_TOKENS")
public class JwtRefreshToken {
#Id
#Column(name = "TOKEN")
private String token;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", nullable = false)
private Tbluser user;
#Column(name = "EXPIRATIONDATETIME")
private LocalDateTime expirationDateTime;
public JwtRefreshToken(String token) {
this.token = token;
}
}
and the the corresponding repository is
JwtRefreshToken findByTokenAndAndExpirationDateTimeBefore( String token, #Param("expirationDateTime") LocalDateTime localDateTime);
The interesting thing here is the query always returns value even though the time has exceeded.
for example the value stored in database is 2019-04-21 22:33:08
and my current date time is 2019-04-21T23:02:43.971
but yet the above findByTokenAndAndExpirationDateTimeBefore returns value.
i want to compare the time as well.
You can enable debug output to see parameterized query and its parameters, add to your application properties
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
Probably, you will get some clues if query does not use #Param("expirationDateTime") LocalDateTime localDateTime at all or there is timezone issue or everything is fine and you just misinterpret results ;)
I have a table in my Postgres database which has a timestamp column. I would like to have it be automatically inserted every time I update a row. I wrote a database trigger:
CREATE FUNCTION update_last_edit_date() RETURNS trigger AS $update_last_edit_date$
BEGIN
NEW.last_edit_date := localtimestamp(0);
RETURN NEW;
END;
$update_last_edit_date$ LANGUAGE plpgsql;
CREATE TRIGGER update_last_edit_date BEFORE UPDATE ON employee
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE update_last_edit_date();
Which works fine but I was wondering if there was an easier way to do this with jpa/hibernate annotations. I tried these different options:
#Preupdate
#PreUpdate
private void onUpdate(){
this.lastEditDate = new Date();
}
#UpdateTimestamp
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
private Date lastEditDate;
But what I get is that when I update one row, the timestamps for all of the rows updated, so all of the timestamps in the table are always the same. What am I doing wrong here?
There are many ways to achieve this goal.
#EntityListener
You can have an #Embeddable to store the audit properties:
#Embeddable
public class Audit {
#Column(name = "created_on")
private LocalDateTime createdOn;
#Column(name = "updated_on")
private LocalDateTime updatedOn;
//Getters and setters omitted for brevity
}
Which requires an EntityListener that looks as follows:
public class AuditListener {
#PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if(audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setCreatedOn(LocalDateTime.now());
}
#PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(LocalDateTime.now());
}
}
Your entities will have to implement the Audit interface:
public interface Auditable {
Audit getAudit();
void setAudit(Audit audit);
}
And the entities will look like this:
#Entity(name = "Tag")
#Table(name = "tag")
#EntityListeners(AuditListener.class)
public class Tag implements Auditable {
#Id
private String name;
#Embedded
private Audit audit;
//Getters and setters omitted for brevity
}
This is a very elegant solution since it extracts the audit logic from the main entity mapping.
#PrePersist and #PreUpdate
You can use the #PrePersist and #PreUpdate JPA annotations as well:
#Embeddable
public class Audit {
#Column(name = "created_on")
private LocalDateTime createdOn;
#Column(name = "updated_on")
private LocalDateTime updatedOn;
#PrePersist
public void prePersist() {
createdOn = LocalDateTime.now();
}
#PreUpdate
public void preUpdate() {
updatedOn = LocalDateTime.now();
}
//Getters and setters omitted for brevity
}
and add the Audit embeddable to the entity like this:
#Entity(name = "Tag")
#Table(name = "tag")
public class Tag {
#Id
private String name;
#Embedded
private Audit audit = new Audit();
//Getters and setters omitted for brevity
}
Hibernate-specific #CreationTimestamp and #UpdateTimestamp
#CreationTimestamp
#Column(name = "created_on")
private Date createdOn;
#Column(name = "updated_on")
#UpdateTimestamp
private Date updatedOn;
That's it!
Now, related to your comment:
But what I get is that when I update one row, the timestamps for all of the rows updated, so all of the timestamps in the table are always the same. What am I doing wrong here?
The timestamp will only be updated for the entity that gets modified, not for all rows. It does not make any sense to update the timestamp of all rows when only a single row gets modified. Otherwise, why would you have that column on the row itself?
If you want the last modification timestamp, just run a query like this:
SELECT MAX(updated_on)
FROM tags
If you are using spring-data then you do this over the auditing feature. Checkout #EnableJpaAuditing or read this article https://www.baeldung.com/database-auditing-jpa
I have the following JPA Entity:
#EntityListeners(AuditingEntityListener.class)
#Entity
public class EntityWithAuditingDates {
#Id
#GeneratedValue
private Long id;
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date createdDate;
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private Date lastModified;
private String property;
// getters and setters omitted.
}
And the following CrudRepository:
#Service
public interface EntityWithAuditingDatesRepository extends CrudRepository<EntityWithAuditingDates, Long> {
}
And the following test:
#SpringApplicationConfiguration(classes = FooApp.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class AuditingEntityListenerTest {
#Autowired
private EntityWithAuditingDatesRepository entityWithAuditingDatesRepository;
#Test
public void test() {
EntityWithAuditingDates entityWithAuditingDates = new EntityWithAuditingDates();
entityWithAuditingDates.setProperty("foo");
assertNull(entityWithAuditingDates.getCreatedDate());
assertNull(entityWithAuditingDates.getLastModified());
entityWithAuditingDatesRepository.save(entityWithAuditingDates);
assertNotNull(entityWithAuditingDates.getCreatedDate());
assertNotNull(entityWithAuditingDates.getLastModified());
assertEquals(entityWithAuditingDates.getLastModified(), entityWithAuditingDates.getCreatedDate());
entityWithAuditingDates.setProperty("foooo");
entityWithAuditingDatesRepository.save(entityWithAuditingDates);
assertNotEquals(entityWithAuditingDates.getCreatedDate(), entityWithAuditingDates.getLastModified());
}
}
The last condition fails. Shouldn't be the createdDate and the lastModifiedDate be different after updating the entity?
Thanks!
I faced the same issue but figured out a workaround for now. On #Column, I have set updatable=false to exclude create* fields on update.
#CreatedBy
#NotNull
#Column(name = "created_by", nullable = false, length = 50, updatable = false)
private String createdBy;
#CreatedDate
#NotNull
#Column(name = "created_date", nullable = false, updatable = false)
private ZonedDateTime createdDate = ZonedDateTime.now();
#LastModifiedBy
#Column(name = "last_modified_by", length = 50)
private String lastModifiedBy;
#LastModifiedDate
#Column(name = "last_modified_date")
private ZonedDateTime lastModifiedDate = ZonedDateTime.now();
It's not necessary to do another query to see fields updated. The repository's save method returns an object, which the documentation says that you should always use for further operations. The returned object should pass that last assertion. Try this:
entityWithAuditingDates = entityWithAuditingDatesRepository.save(entityWithAuditingDates);
If you retrieve the entity from the database after the update operation, the fields are set correctly. The test case below passes. Still, I wonder why they are set correctly on the first save operation, but then incorrectly upon the second. And you only get the correct information in the end when you retrieve the record from the database. I guess this is related to the hibernate cache.
#Test
public void test() throws InterruptedException {
EntityWithAuditingDates entityWithAuditingDates = new EntityWithAuditingDates();
entityWithAuditingDates.setProperty("foo");
assertNull(entityWithAuditingDates.getCreatedDate());
assertNull(entityWithAuditingDates.getLastModified());
entityWithAuditingDatesRepository.save(entityWithAuditingDates);
assertNotNull(entityWithAuditingDates.getCreatedDate());
assertNotNull(entityWithAuditingDates.getLastModified());
assertEquals(entityWithAuditingDates.getLastModified(), entityWithAuditingDates.getCreatedDate());
entityWithAuditingDates.setProperty("foooo");
Thread.sleep(1000);
entityWithAuditingDatesRepository.save(entityWithAuditingDates);
EntityWithAuditingDates retrieved = entityWithAuditingDatesRepository.findOne(entityWithAuditingDates.getId());
assertNotNull(retrieved.getCreatedDate());
assertNotNull(retrieved.getLastModified());
assertNotEquals(retrieved.getCreatedDate(), retrieved.getLastModified());
}
I have a SQLServer and am using hibernate to map an entity to a table that I already have.
the table has a field of type "timestamp" [field_date] timestamp NOT NULL which is the only date the table has.
I have tried mapping it on an entity like this:
#Column(name="field_date")
private Date date;
and even
#Column(name="field_date",columnDefinition="TIMESTAMP")
private Date date;
but every time I try to do a select on that entity I get an SQLServerException of type
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The conversion from timestamp to TIMESTAMP is unsupported.
1) Try mapping it to the Java class java.sql.Timestamp.
http://docs.oracle.com/javase/7/docs/api/java/sql/Timestamp.html
Also here I would try removing this from your annotation
columnDefinition="TIMESTAMP"
2) Here is another mapping to try.
So alternatively you can try this approach.
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CreatedOn", nullable = false)
public Date getCreatedOn() {
return this.createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
3) This is like 2) but it is to be used if you want
mapping for the class field (not for the getter).
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "Last_Updated_Date", nullable=false)
private Date lastUpdatedDate;