JPA 2.0 and Oracle with TemporalType.TIME - oracle

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

Related

Find all entities with dates between, spring data jpa [duplicate]

I'm trying to make a query to retrieve some data which has been created between two dates (represented as Instant).
Here below an extract from the Entity I'm using:
#Entity
public class HistoricalData {
#Id
#GeneratedValue
private Long id;
#Column
private String name;
#CreationTimestamp
private Instant timestamp;
#Column
private Double price;
}
And the query I've written to retrieve the data between the two Instants;
#Query("select h from HistoricalData h where h.timestamp between :timestampStart and :timestampEnd and upper(name) = upper(:name)")
List<HistoricalData> findHistoricalDataBetween(#NonNull Instant timestampStart, #NonNull Instant timestampEnd, #NonNull String name);
Which produces this SQL query:
select historical0_.id as id1_5_, historical0_.price as price2_5_, historical0_.timestamp as timestam3_5_ from historical_data historical0_ where (historical0_.timestamp between ? and ?) and upper(historical0_.name)=upper(?)
Also I wrote the "hibernate JPA" query just to try but no success:
List<HistoricalData> findHistoricalDataByTimestampAfterAndTimestampBeforeAndName(#NonNull Instant timestampStart, #NonNull Instant timestampEnd, #NonNull String name);
Keep in mind that all the above queries compile correctly and do not throw any exception, they just retrieve nothing from the database
The database I'm using is a latest version of MariaDB and the connector version is the 2.7.2
Also the SpringBoot version I'm using is the 2.5.3
Here is DDL from the table definition (automatically generated from Hibernate):
create table historical_data
(
id bigint not null primary key,
price double null,
timestamp datetime not null,
name varchar not null
);
An this is how the timestamp looks like in the database:
Even though records between those two Instants are present in the database I'm still getting nothing as a result from the query.
Looks like the reason is a time zone.
MySQL driver uses incorrect time zone transformations, using a default local time zone in place of a connection time zone (or vice versa).
Just debug this query inside MySQL driver to have fun and figure out what happens.
You can add parameters to the database URL to see which actual values are passed for the prepare statement
jdbc:mysql://<DATABASE_URL>?logger=com.mysql.cj.log.Slf4JLogger&profileSQL=true

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

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.

Oracle DB queries through JPA/Hibernate from java.util.Date

I am currently working on a migration of a Spring Boot application from MariaDB to OracleDB. The Spring/Java backend uses Hibernate/JPA to generate queries for the MariaDB database, and as such in theory the migration should be fairly painless. Change a dialect and you're done. In practice, it turns out that the hibernate dialect for OracleDB 12C makes some odd assumptions when it comes to binding types to database types. The backend still uses the old java.util.Date type for all of its dates, which Hibernate seems to want desperately to cast to either a Long (even more outdated as far as I could find) or a BLOB type of some sort. BLOBs are great of course, but it seems much more intuitive to map a Date to a DATE.
Because the row is currently set to expect a DATE, I get the following error whenever I try to access the row:
InvalidDataAccessResourceUsageException: could not extract ResultSet
ORA-00932: inconsistent datatypes: expected - got BLOB
I have tried using the JPA Converter feature to manually cast these Date objects to something Hibernate wouldn't mess up, but this resulted in Hibernate expecting a VARBINARY as this article describes:
https://dzone.com/articles/leaky-abstractions-or-how-bind
#Converter(autoApply = false)
public class DateDATEAttributeConverter implements AttributeConverter<Date, DATE> {
#Override
public DATE convertToDatabaseColumn(Date date){
return new DATE(); // conversion to be done later
}
#Override
public Date convertToEntityAttribute(DATE date) {
return new Date(); // conversion to be done later
}
}
Using this very minimal converter, and running through the code step-by-step with a debugger, shows that everything seems to be properly attached to the preparedstatement, but is then refused by Hibernate with an
org.hibernate.exception.SQLGrammarException: could not extract ResultSet
error.
Afterwards I decided to try making a customer UserType like in the above article, and described in further detail here:
https://www.baeldung.com/hibernate-custom-types
I currently cast the java.util.Date to an Oracle DATE through the use of this custom type. The #Type annotation is used to make sure the relevant field is converted using this CustomType implementation. Sadly, after implementing all this, the same error as before returns. It seems somewhere beneath the hood there is still a conversion/binding going on that I haven't managed to influence.
#Override
public Object nullSafeGet(
ResultSet rs,
String[] names,
SessionImplementor session,
Object owner
)
throws SQLException {
LOGGER.debug("nullSafeGet: " + names[0]);
return rs.getTimestamp(names[0]);
}
#Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SessionImplementor session
)
throws SQLException {
if(Objects.isNull(value)){
st.setNull(index, Types.DATE);
}else{
Date dateUtil = (Date)value;
LocalDate localDate = dateUtil.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
java.sql.Date dateSQL = java.sql.Date.valueOf(localDate);
DATE date = new DATE(dateSQL);
LOGGER.debug("nullSafeSet: " + date);
st.setObject(index, date);
}
}
Is there any established method to get around this? I have searched around online for quite a bit, but I didn't get much further than these two articles, or being told to stop using old types such as Date. Sadly with big old projects and new deadlines that is not a preferable option.

spring boot - calculated field

So, I have an entity, that has field start_date (java.util.Date is the type).
I want to have another field, that would automatically populate with integer that corresponds to day of week (as a number 1 for sunday, 2 for monday, etc.) of starting date.
Here's fragment of my entity:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(TemporalType.DATE)
private Date start_date;
I've tried to add the calculated field in the following way:
#Column(name = "weekday")
#Formula("(select dayofweek(l.start_date) from Lesson l where l.id = id)")
private Integer weekDay;
However, when looking at the Lesson table in H2 console, there's no such column as "weekday"
I also tried other option - without #Formula annotation and with a setter that takes start_date parameter, but I guess this setter is never called, since the column "weekday" is populated with null.
Here's the setter I've tried to use as an alternative solution:
public void setWeekDay(Date start_date) {
Calendar c = Calendar.getInstance();
c.setTime(start_date);
this.weekDay = c.get(Calendar.DAY_OF_WEEK);
}
It's obvious that I'm missing something here, it's probably because I'm still learning Spring boot...
To sum up - I want to have a column in table Lesson, that is calculated from another column of the same table.
#Formula means your field to be calculated by your rules. This entity field is not column in db. This field is calculating for each entity in loading time by specified rule.
If annotation #Column(name = "weekday") would work near #Formula you would be really confused if you expect in loaded entity same value as in DB but here is calculated one and different (inconsistent situation).
If you want save here value from the Lesson table you should remove #Formula and use #EntityListeners({YourEntityJpaCallbacksListener.class}) In spring bean YourEntityJpaCallbacksListener you can define methods marked with #PreUpdate or #PrePersist and use correspond operations to set calculated value into weekday.
for example:
#EntityListeners({YourEntityJpaCallbacksListener.class})
#Entity
public class YourEntity{
// your code
}
#Component
public class YourEntityJpaCallbacksListener {
#Autowired
private LessonRepository lessonRepository;
#PreUpdate
void preUpdate(YourEntity yourEntity) {
if (recurrentRuleRepository.exists(yourEntity.getId())) {
Integer weekDay = lessonRepository.findOne(yourEntity.getId());
yourEntity.setWeekDay(weekDay);
}
}
}
Ok, so I think I have managed to solve this.
I have removed the #Formula annotation
I have created a method that calculates the day of the week and writes it to weekDay.
public void calculateDayOfWeek(){
Calendar c = Calendar.getInstance();
c.setTime(start_date);
this.weekDay = c.get(Calendar.DAY_OF_WEEK);
}
And I put it inside start_date setter:
public void setStart_date(Date start_date) {
this.start_date = start_date;
calculateDayOfWeek();
}
So now, evertime the start_date is created or updated, it updates (or creates) the value of weekDay.

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

Resources