Is there simple way of changing timezone between dto and entities at database? - spring

I write application on Spring Boot with Spring Data(postgresql).
I have the following case. I want to store in database time at UTC timezone, and parse it to/from "America/San-Paulo" timezone in dto.
For example: in controller I get dto with LocalDateTime in America/San-Paulo timezone. And I want to save it in database in UTC timezone.
I can do in when mapping from dto to entity. But maybe there is another simple way like setting some properties of hibernate/spring?

Since Java 8, we have the Date/Time API under java.time!
(1) Convert the timezone in annotated #PrePersist, #PreUpdate, and #PostLoad methods.
For example, in annotated #PostLoad, convert from UTC to America/San-Paulo.
private static ZoneId UTC_ZONE = ZoneId.of("UTC");
private static ZoneId LOCAL_ZONE = ZoneId.of("America/San_Paulo");
private LocalDateTime dateTime;
#PostLoad
public void toLocal() {
dateTime = dateTime.atZone(UTC_ZONE).withZoneSameInstant(LOCAL_ZONE).toLocalDateTime();
}
(2) Assuming you are using Jackson, you can write a custom serializer/deserializer.
UPDATE:
With PostgreSQL, you can use the type timestamp with time zone. By default, if you insert/update the column, it will convert the value to UTC.
In JPA:
#Column(columnDefinition = "timestamp with time zone")
UPDATE (22-07-01):
You could also use an AttributeConverter.

Related

Converting java.sql.timestamp to millisecond format

I have a private java.sql.Timestamp myDate in some model (POJO) class like below
private String isActive;
private Date dob;
private Timestamp createdDate;
private Timestamp lastMktPrefUpdateAt;
We were using spring boot version to 1.5.10.RELEASE and REST API response for timestamp field was in millisecond format like below -
{
"isActive": "y",
"lastMktPrefUpdateAt": 1632195609787,
"dob": "08/12/1991",
"createdDate": 1632195609788
}
We have recently upgraded the spring boot version to 2.3.0.RELEASE and this started sending timestamp field in response to some ISO format like below -
{
"isActive": "y",
"lastMktPrefUpdateAt": "2021-09-20T22:10:09.787+0000",
"dob": "07/12/1991",
"createdDate": "2021-09-20T22:10:09.788+0000"
}
Can someone please help answering, how can we format Timestamp back to millisecond format?
We need to format Timestamp at global level meaning without changing all the POJO classes.
Try this in your properties file
spring.jackson.serialization.write-dates-as-timestamps:true
OR
Use #JsonFormat(shape = JsonFormat.Shape.NUMBER) above the property that you want.
Update
If you want to add millis to your timestamp, try to pass the long value to the Timestamp constructor and call the getTime() method to receive a 13-digits timestamp.
long now = System.currentTimeMillis();
Timestamp timeStamp= new Timestamp(now);
System.out.println(timeStamp.getTime());
You could convert that string to a ZonedDateTime object which has a .toInstant().toEpochMilli() method.
Something like:
long millis = ZonedDateTime.of(LocalDateTime.parse(str), ZoneId.of("Etc/UTC")).toInstant().toEpochMilli();
see:
How to get milliseconds from LocalDateTime in Java 8
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html
However, I would recommend refactoring the system to use the immutable LocalDatTime object at a later point.
(There may be an appropriate annotation you can put on that timestamp field to parse it in a specific way, but not one that I am aware of.)

Inserting a ZonedDateTime value to Oracle Timestamp With Timezone Column With JPA

In my SpringBoot project, I am trying to insert a ZonedDateTime value to an Oracle database where the column type is specified as TIMESTAMP WITH TIMEZONE. When I do that, the inserted value gets converted to the JVM timezone and gets inserted. However, what I expect is to insert the value in ZonedDateTime property as it is.
For example, if the ZonedDateTime value is set to 2020-05-16T12:12:24+02:30[US/Denver], then I expect to write to DB the same value and read from DB the same value. Is there any way I can do this with Hibernate and JPA
#Entity
class MyEntity{
ZonedDateTime myDateTimeWithTimeZone;
}
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long>
{
}
This is how i am going to use it,
MyEntity myEntity = new MyEntity();
myEntity.setMyDateTimeWithTimeZone(ZoneDateTime.now('US/Denver')) // This timezone can be anything depending on userInput
myRepository.save(myEntity);

Spring Data MongoDB document expiry time

I've set up a simple document model (below) along with a Spring Data repository
#Document(collection = "users")
public class UserDocument {
#Id
private String userId;
#Indexed(expireAfterSeconds=3600)
private LocalDateTime registeredDate;
}
This seems to work fine and deletes the user documents after whatever time I set expireAfterSeconds to. However, rather than typing in a value of 3600 I'd like to pull that number from a config .yml. The usual way of adding #Value("${config.file.path.of.expiry}") won't work because #Indexedrequires the value to be a runtime constant, which #Value isn't.
Is there any other way to set up document expiry dates in Spring/Mongodb that doesn't use #Indexed(expireAfterSeconds=x)?
Instead of using the Indexed annotation to declare an index which Spring Data Mongo will create on your behalf ... you could create the index explicitly using a MongoTemplate instance.
#Value("${config.file.path.of.expiry}")
long expireAfterSeconds;
...
mongoTemplate.indexOps("users").ensureIndex(
new Index().on("registeredDate", Order.ASCENDING)
.expire(expireAfterSeconds)
);
This would allow you to source the value for expireAfterSeconds from a configuration file at runtime rather than hardcoding it in an annotation.

How to store Joda DateTime in MySQL

I've recently started using Joda time library for my test project.
Particularly i have been enjoying the capabilities of DateTime and functions for its manipulation.
My query is how do you store DateTime in MySql. I am using Spring & Hibernate for my application.
my current entity throws deserialisation errors whenever I try and use it:
#Entity
#Table(name = "test_storage")
public class TestEntity {
#Id #GeneratedValue
private int id;
#Column
private DateTime testDate;
//getters and setters
}
The mysql table structure is as follows:
Name: test_storage
Columns:
id INT NOT_NULL, AUTO_INCREMENT
testDate DATETIME
Any advice?
If you are using Hibernate 4+, then you can adopt the Jadira user types which allow you to map DateTime (and other JODA date time related class like LocalDate, LocalDateTime etc) to DB fields using different strategies.
Your mapping will look like
public class TestEntity {
//...
#Column
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime testDate;
}
Read the documents to know how to properly use these types to fit your requirements.
The biggest pitfall that you may face soon is, as Java's Date does not include timezone information nor does it sticks to UTC (JODA's user types still need to map to Timestamp/Date internally), you may want to make sure the way you store does provide proper information. For example, either store the date time as UTC, or store timezone information as a separate field, etc.
DATETIME would be my choice. See some more details at What difference between the DATE, TIME, DATETIME, and TIMESTAMP Types and http://infopotato.com/blog/index/datetime_vs_timestamp

JPA 2.0 and Oracle with TemporalType.TIME

I'm Using Oracle 11g and JPA 2.0 (hibernate in JBoss 6.0.0).
I need to represent a time range in an entity, so I defined those fields:
#Temporal(TemporalType.TIME)
private Date startTime;
#Temporal(TemporalType.TIME)
private Date endTime;
The generated tables use two DATE fields, and this is ok since Oracle doesn't have a type representing just the time part.
When loading the entity from db, just the time part is loaded (the field contains a java.sql.Time).
I've seen instead that if I set a complete date+time in the fields, the date part will be persisted to the db.
Is there a way to ensure that the date part will not be persisted to the db?
You can write setter methods which remove the date component. Quick and dirty example:
public void setStartTime(Date startTime)
{
this.startTime = new Time(startTime.getTime() % 86400000L);
}
Though you'd be better off using Joda Time to do your date/time calculations (see this question). I didn't test this to make sure it's correct, but it should show you the basic idea:
public void setStartTime(Date startTime)
{
this.startTime = new Time(LocalTime.fromDateFields(startTime).millisOfDay().get());
}

Resources