Oracle "TIMESTAMP WITH TIMEZONE" Java type mapping issue with raw sql query - oracle

I am using an Oracle 11.2 database with column types "TIMESTAMP WITH TIMEZONE" to store time relevant information. I can't change it because it is used by an existing system.
The Java application uses ojdbc7 (v12.1.0.2) + hibernate as JPA provider.
I simplified the following example for easer understanding!
There is an entity "Product" which maps "TIMESTAMP WITH TIMEZONE" columns to java.util.Calendar
#Entity
#Table(name = "PRODUCT")
public class Product {
#Id
#Column(name = "ID")
private String id;
#Column(name = "START_DATE", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private Calendar startDate;
....
}
If I load it through a HQL query it works fine.
1. Issue
If I use a raw sql query:
SELECT * FROM PRODUCT where id='123456';
I get the following exception:
org.hibernate.MappingException: No Dialect mapping for JDBC type: -101
The sql query is a lot more complex I just used a very simple example here. So I can not simply convert it to an HQL query.
2. Issue
If I persist a new product with a start date having a time zone like AEDT. E.g.
2014-01-01 12:00:00 AEDT
it get saved in the database as
01-JAN-14 01.00.00.000000000 UTC
If I query it again from the Java application the Calendar object has the time in UTC as shown above and not the time in AEDT. It is not just a display issue.
The conversion from AEDT to UTC is correct, but the problem is that the information is lost that it was inserted with timezone AEDT.
Is there a way to prevent this? Why is this happening anyway? I searched a lot today, but could not find a good hint.

You can solve the first issue the following way:
First, you need to unwrap the Query implementation. You said that you were using JPA + Hibernate, so:
Query jpaQuery = entityManager.createNativeQuery("select, t.int_c, t.str_c, t.timestamp_c from table t");
SQLQuery hibernateQuery = jpaQuery.unwrap(SQLQuery.class);
(org.hibernate.SQLQuery.class is the wrapped class)
Second, you have to set the type for ALL the columns that you want to retrieve:
hibernateQuery.addScalar("int_c", IntegerType.INSTANCE);
hibernateQuery.addScalar("str_c", StringType.INSTANCE);
hibernateQuery.addScalar("timestamp_c", TimestampType.INSTANCE);
Third, just make the query using the original JPA Query instance
List<Object[]> results = jpaQuery.getResultList();

Related

Select Datetime as Date in criteria query

I'm using JPA CriteriaBuilder to select attributes from the database. I have a specific use case that involves Conversion of datetime to date. The Projection segment of the code is as follows.
CriteriaQuery<ResponseDTO> query = cb.createQuery(ResponseDTO.class);
query.multiselect(root.get(MasterPackagesV1_.masterPackageName),
packageJoin.get(PackagesV1_.packageId),
packageJoin.get(PackagesV1_.packageName),
packageJoin.get(PackagesV1_.startSellingDate),
packageJoin.get(PackagesV1_.endSellingDate),
packageJoin.get(PackagesV1_.createdDate),
packageJoin.get(PackagesV1_.modifiedDate),
packagePaymentJoin.get(PackagePaymentPlanV1_.mrp),
packageSubscriptionJoin.get(PackageSubscriptionsV1_.packageSubscriptionId)
);
All of the date attributes are stored as datetime datatype in the MySQL database. I'm want the datetime to be formatted to date. i.e Instead of retrieving the Dates as LocalDateTime I want it to be converted to LocalDate during Instantiation.
I have made changes in the ResponseDTO class accordingly to support LocalDate. Thanks in Advance.

How to Encrypt A Certain Column automatically with ColumnTransformer?

#ColumnTransformer(forColumn = "description", read = "pgp_sym_decrypt(description, current_setting('encrypt.key'))", write = "pgp_sym_encrypt(?, current_setting('encrypt.key'))")
#Column(name = "description", length=6000)
#Lob
#Type(type = "org.hibernate.type.TextType")
private String description;
I have this column transform where I encrypt the column before saving to the database.
Sometiems it works but most of the time it shows this error. The database not just this, and it works for other columns. Only on this particular column, it wont work. Do you have ideas? Could this be server related issue? As the application works on the test and local machines
column "description" is of type bytea but expression is of type character varying. Hint: You will need to rewrite or cast the expression.
Remove the #Lob annotation, unless the DDL type is oid. Other than that, the mapping looks good to me. Share the stack trace of the error and the failing SQL in case this doesn't help.

Wrong LocalDate stored with hibernate

I'm trying to store inside the database the date of a restaurant booking but, even though the date I submit is correct, hibernate stores inside the database a date one day before the one I submitted. I don't know why... it's probably a timezone problem but I can't understand why... the date should not be affected by the timezones.
Here is my spring boot properties file:
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
jpa:
database: MYSQL
hibernate:
ddl-auto: update
properties:
hibernate:
locationId:
new_generator_mappings: false
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
jdbc:
time_zone: UTC
datasource:
driver:
class: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: username
password: **********
I'm from Italy, so my timezone is this:
GMT/UTC + 1h during Standard Time
GMT/UTC + 2h during Daylight Saving Time
Currently we are UTC + 2h.
The object I'm storing is this one:
#Entity
public class Dinner {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long dinnerId;
private LocalDate date;
...
The controller I'm using to intercept the POST request is this:
#PreAuthorize("hasRole('USER')")
#PostMapping
public String createDinner(#RequestParam(value="dinnerDate") String dinnerDate, Principal principal, Model model){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(dinnerDate, formatter);
dinnerService.createDinner(date);
return "redirect:/dinners?dinnerDate=" + dinnerDate;
}
Which calls the service method createDinner that call the Jpa method save to store the object.
I'm using thymeleaf to handle the html templates.
If I submit the date 30/6/2019 inside the database I get 29/6/2019. When I retrieve the Dinner object by date, if I insert 30/6/2019, I get the Dinner with the date 29/6/2019. So it seems that spring handle the date by itself in a weird way... considering some sort of timezone but I don't know how to disable or handle it. Any idea?
You do not need to define a format for the pattern yyyy-MM-dd. LocalDate#parse uses DateTimeFormatter.ISO_LOCAL_DATE by default which means LocalDate.parse("2020-06-29") works without applying a format explicitly.
Since you already know that date-time in your time-zone is different from that in UTC, you should never consider just a date; rather you should consider both date and time e.g. 11:30 PM at UTC on 2020-06-29 will fall on 2020-06-30 in your time-zone. Therefore, the first thing you should do is to change the type of the field as TIMESTAMP in the database.
Once you have changed the type of the field to TIMESTAMP, change the method, createDinner as follows:
LocalDateTime dinnerDateTime = LocalDateTime.of(LocalDate.parse(dinnerDate), LocalTime.of(0, 0, 0, 0));
OffsetDateTime odt = dinnerDateTime.atOffset(ZoneOffset.UTC);
dinnerService.createDinner(odt);
Then inside DinnerService (or DinnerServiceDAO wherever you have written the logic to insert/update record in the database):
pst.setObject(index, odt);
where pst represents the object of PreparedStatement and index represents the index (starting with 1) of this field in your insert/update query.
same problem (and same country! :-) ).
I suspect that if hibernate or jpa are set with timezone UTC, while the machine is set with default timezone == Europe/Rome when a date is persisted, then it will be converted automatically from machine timezone to database timezone, which is not a bad feature if you have all dates stored in UTC format on the DB.
The problem happens when you convert the date before persisting: it gets converted twice. At least, this is my case.
Still looking for the best solution! In case I'll find one, then I'll add it later to the answer.
Assuming your time zone is : Europe/Italy , You have to set up your serverTimezone variable like this :
url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Italy

Query by Example Spring Data

I have domain object Person with date fields:
public class Person {
#Id
private Long id;
private Date date
Build example like this:
Person person = new Person();
person.setSomeOtherFields("some fields");
Example<Person> example = Example.of(person);
How i can create example query with date range (search entity contains date greater or equal from some date and less or equal some another date)?
The Spring Data JPA query-by-example technique uses Examples and ExampleMatchers to convert entity instances into the underlying query. The current official documentation clarifies that only exact matching is available for non-string attributes. Since your requirement involves a java.util.Date field, you can only have exact matching with the query-by-example technique.
You could write your own ExampleMatcher that returns query clauses according to your needs.

Extracting timestamp field in Oracle vs MySQL from Grails-Groovy

I am using grails/groovy, and from my controller I am currently doing this for retrieving field from Mysql table containing datetime field
SimpleDateFormat Sformat = new SimpleDateFormat("yyyy-MM-dd");
String format_datenow = Sformat.format(new Date());
String format_dateprevious = Sformat.format(new Date() -31);
String markerCalcQuery =
"select sum(trans_cnt) as t_cnt, location from map2_data where fdate between '"+format_dateprevious+"' and '"+format_dateprevious+"' and res_id = "+res_id+" group by map2_data.location";
res_row=gurculsql.rows(markerCalcQuery);
The above query fails on Oracle11g with error
ORA-01843: not a valid month.
The error I feel is because MySQL stores date in this format: 2011-12-28 02:58:26 and Oracle stores date like this: 28-DEC-11 02.58.26.455000000 PM
How do I make the code generalised, one way is to make the database in Oracle store the date in the same format which I am thinking the way to handle this rather than from the code. If yes, how to change date format in the Oracle db?
Can I specify the format in the grails domain class for map2_data so that no matter what database it is we will have the datetime in the same format.
For several reasons (one being to code database independent - which is basically what you'd need ;-)), it is better to avoid creating SQL statements in your code. Try to use the Grails criteria DSL, e.g. something like
def criteria = YourDomainObject.createCriteria()
criteria.get {
between ('fdate', new Date()-31, new Date())
projections {
sum('trans_cnt')
groupProperty('location')
}
}
(ontested, but should help you get started).
If for some reason you can't use the criteria API, try the fallback to HQL (Hibernate Query Language). I'd always try to avoid to write plain SQL.
In Oracle, dates have their own type, they aren't strings. If you have a string, you should convert it to a date using the TO_DATE function.
String format_datenow = "TO_DATE('" + Sformat.format(new Date()) + "', 'YYYY-MM-DD')";
To make it work also in MySQL, you can create a stored function named TO_DATE that just returns its first argument.

Resources