JPQL not working with dates - java

I'm not able to get the data from database using the dates in the query...
I'm working on Web application which is using Spring Data JPA and Oracle Database. I was using #RepositoryRestResource annotation in interface where I was just declaring some query methods with named parameters using #Param and #Query annotations. Today I needed to add a new entity with the dates. In database both columns are type of DATE and it is used in the query. But I also have the other one which is type of TIMESTAMP and maybe I would need to use in a future. And below the Java representation of this two columns only, of course with all setters and getters, but it has more fields so just adding this:
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "INIT_DATE")
private Calendar initDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "AGG_DATE")
private Calendar aggDate;
I also created new interface for case, the same way as always:
#RepositoryRestResource(collectionResourceRel = "customer", path = "customer")
public interface ICustomerRepository extends PagingAndSortingRepository<Customer, Long> {
#Query("SELECT c FROM Customer c where c.initDate <= TO_DATE(:currentDate, 'yyyy-MM-dd') AND c.aggDate >= TO_DATE(:currentDate, 'yyyy-MM-dd')")
public List<Customer> filterByDate(#Param("currentDate") #DateTimeFormat(pattern = "yyyy-MM-dd") Calendar currentDate);
}
And I'm receiving this error:
org.springframework.dao.DataIntegrityViolationException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet
ORA-01858: a non-numeric character was found where a numeric was expected
I'm trying to get this data from database using this http request:
http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10
In SQL Developer the query works fine.
I read somewhere that in JPQL there is no date function, but in log I can see the query and parameter which looks like this:
select
customer0_.customerId as col_0_0_,
customer0_.customerName as col_0_1_,
customer0_.aggDate as col_0_2_,
customer0_.initDate as col_0_3_,
from
customer customer0_
where
and customer0_.aggDate>=to_date(?, 'yyyy-MM-dd')
and customer0_.initDate<=to_date(?, 'yyyy-MM-dd')
2017-07-25 11:55:22.550 TRACE 12252 --- [ (self-tuning)'] o.h.type.descriptor.sql.BasicBinder : binding parameter [8] as [TIMESTAMP] - [java.util.GregorianCalendar[time=1499637600000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=6,WEEK_OF_YEAR=28,WEEK_OF_MONTH=3,DAY_OF_MONTH=10,DAY_OF_YEAR=191,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=3600000,DST_OFFSET=3600000]]
2017-07-25 11:55:22.550 TRACE 12252 --- [ (self-tuning)'] o.h.type.descriptor.sql.BasicBinder : binding parameter [9] as [TIMESTAMP] - [java.util.GregorianCalendar[time=1499637600000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=6,WEEK_OF_YEAR=28,WEEK_OF_MONTH=3,DAY_OF_MONTH=10,DAY_OF_YEAR=191,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=3600000,DST_OFFSET=3600000]]
And to be honest, I have no idea what is the problem here... The format date in the database is yy/MM/DD, but it also wasn't working for me... Could you tell me what I'm missing or doing wrong??
EDIT [answer to Gusti Arya]:
I tried two things. Before your update I just changed the type and left the #DateTimeFormat. Then I had the same error as with Calendar type.
After removing the #DateTimeFormat, so having the same after your update I get this error:
org.springframework.data.repository.support.QueryMethodParameterConversionException: Failed to convert 2017-07-10 into java.util.Date!
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.springframework.data.repository.query.Param java.util.Date] for value '2017-07-10'; nested exception is java.lang.IllegalArgumentException
What is more interesting, I have the second query which is almost the same, but without TO_DATE function and now I have the same error as above. Previously I had:
Persistent Entity Must not be null
EDIT 2 [related with the query in the log]:
I just noticed that the query which I posted here is not the same as I see in the log... My entity contains EmbeddedId in which is customerId and customerName. And those two columns appears thrice... So here is the valid log of the query:
select
customer0_.customerId as col_0_0_,
customer0_.customerName as col_0_1_,
customer0_.customerId as col_1_0_,
customer0_.customerName as col_1_1_,
customer0_.customerId as customerId1_6_,
customer0_.customerName as customerName2_6_,
customer0_.aggDate as aggDate3_6_,
customer0_.initDate as initDate4_6_,
from
customer customer0_
where
and customer0_.aggDate>=to_date(?, 'yyyy-MM-dd')
and customer0_.initDate<=to_date(?, 'yyyy-MM-dd')
**EDIT [response to Brian]: **
And also the types in Entity should be Calendar, right? I also put two different Temporal annotation for those two fields. One points to TemporalType.TIMESTAMP and the other to TemporalType.DATE. But then is the problem with the passing calendar value as http parameter. I tried four versions of URL:
1. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10
2. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10 13:08:24.000+0000
3. http://localhost/webApp/customer/search/filterByDate?currentDate=2017-07-10T13:08:24.000+0000
4. http://localhost/webApp/customer/search/filterByDate?currentDate=1499692104
But none of this is not working...

Without further digging into detail, I think you just need to change
#Query("SELECT c FROM Customer c where c.initDate <= TO_DATE(:currentDate, 'yyyy-MM-dd') AND c.aggDate >= TO_DATE(:currentDate, 'yyyy-MM-dd')")
to
#Query("SELECT c FROM Customer c where c.initDate <= :currentDate AND c.aggDate >= :currentDate")
Why? Because the fields initDate and aggDate are of type java.util.Calendar and so is the parameter currentDate. Everything matches and Spring Data as well as any JPA provider binds java.util.Calendar to an SQL timestamp as you can see in your log
...binding parameter [8] as [TIMESTAMP]...

I made huge mistake... The problem is not with the dates, but with the query and the entity... In my real application I have entity with #EmbeddedId and I was sure that the query without filter on date works fine... So I didn't mention about it. Unfortunately, the problem is this #EmbeddedId... I thought that I can use query with c.customerId, c which will return everything, but is not working and without c.customerId returns only Entity without EmbeddedId.
So as #Brian mentioned in his comment:
The ID should be part of the self URL in your JSON response. Including the ID in the JSON object would be redundant. Nonetheless you can expose it via config. See this question and answer for details.
To sum up, query works without TO_DATE function, with #DateTimeFormat annotation for Calendar parameter and #Temporal annotation for the fields with the dates in the entity.
Thank you for all your help and sorry for this mistake.

try changing your code in model class into this :
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "INIT_DATE")
private java.util.Date initDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "AGG_DATE")
private java.util.Date aggDate;
and also in JPQL not defining TO_DATE function, try changing your jqpl to this :
#Query("SELECT c FROM Customer c where c.initDate <= :currentDate AND c.aggDate >= :currentDate"
public List<Customer> filterByDate(#Param("currentDate") Date currentDate);

There is no TO_DATE function in JPQL. You shall either use native sql query, or wrap native database function into JPQL FUNCTION function :)
I believe it should be FUNCTION(TO_DATE('2000-01-01', 'YYYY-MON-DD'))

Related

Got some troubles with JPQL (JPA) query SELECT DISTINCT ORDERED BY

as in title i got some problems with query in JPQL
that's the query I'm using:
#Query(value = "SELECT DISTINCT o.idWhom FROM Message o WHERE o.idWho = ?1 ORDER BY o.date DESC")
List<Users> allCorrespondents(Users user);
Class message:
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotBlank
private String content;
#Temporal(TemporalType.TIMESTAMP)
private Date date = new Date();
boolean read = false;
#ManyToOne
#JoinColumn(name = "id_who")
private Users idWho;
#ManyToOne
#JoinColumn(name = "id_whom")
private Users idWhom;
}
Error I've got:
2022-06-17 13:02:22.435 ERROR 2304 --- [nio-8080-exec-2] c.e.h.S.CustomAuthorizationFilter :
Error logging in: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
Although I am not able to prove it through issue tickets, I have done enough investigation to believe that this issue occurs to hibernate version previous from 5. (Example Hibernate 4!) with database being used Oracle.
What the issue seems to be is the following:
When you have written the JPQL query SELECT DISTINCT o.idWhom FROM Message o you would expect that the entities would be filtered to be distinct after the execution of the SQL query, where the mapper would filter out the distinct entities.
For some reason however in those versions I mention above what actually happens is that the distinct is passed in the SQL query and what actually executes is the following:
SELECT DISTINCT idh.id_column FROM O_TABLE o JOIN IDWHOM_TABLE idh on (o.id_foreign_key_column = idh.id_column
As you can see this select query does not return in result set all columns that are needed from mapper to build the entity but only returns the id column. Therefore the mapper is not able to extract from ResultSet all columns needed to build the entity and you get the error that you get could not extract ResultSet.
A workaround I have found, is that it is able to work when distinct is placed on some primitive field and not on an entity, so you can use this to your advantage to bypass this issue with the following approach:
This supposes that the entity Users has as id a field named id.
#Query(value = "SELECT us FROM Users us where us.id in (SELECT DISTINCT o.idWhom.id FROM Message o WHERE o.idWho = ?1)")
List<Users> allCorrespondents(Users user);
I have excluded the ordering which you have in your original query since even from logical point of view seems wrong. A User entity can have multiple messages. How could you order the returned users just from created date of messages? This field is not able to order Users themselves. It could be used to order messages only.

Unable to use OffsetDateTime as a query parameter using JPA Data/JPQL

I have an Entity CustomEntity with a data member updateDate of type OffsetDateTime.
I have defined a Repository for this Entity which has a simple method to retrieve list of records matching updateDate as
List<CustomEntity> findByUpdateDate(OffsetDateTime updateDate);
Now, when this method is called from Controller/Service bean, I can see no matching record is retrieved; however, when I execute the generated SQL in the DB, I can see matching rows available.
I can retrieve the records based on other data members of the entity; its just an issue with OffsetDateTime and LocalDateTime
I got to understand that java.time package support was not in JPA 2.1; however I am using JPA 2.3.1. Do I need to use Converters (as suggested for JPA 2.1?
Any help is much appreciable.
EDIT :-
Below is the code for Entity
#Entity
#Table(name = "SAMPLE_TABLE")
public class CustomEntity {
#Id
#GeneratedValue
private Long id;
#Column
private OffsetDateTime updateDate;
//Getters & Setters
}
I am using Microsoft SQL Server and the generated SQL query (hibernate generated) looks something like below
select sample0_.id as id1_10, sample0_.updateDate as update2_10 from sample_table sample0_ where sample0_.updateDate=?
binding parameter [1] as [TIMESTAMP] - [2021-07-27T17:22:34.597Z]

How to query with Spring JPA on jsonb columns?

I'm using spring JPA with PostgreSQL database. I have an Entity as follow:
#Entity
#TypeDef(name="json_binary", typeClass = com.vladmihalcea.hibernate.type.json.JsonBinaryType.class)
public class LiveTokens {
#Id
#GeneratedValue()
private Long id;
private String username;
#Type(type="json_binary")
#Column(columnDefinition = "jsonb")
private Token token
}
and Token:
public class Token {
private Long expireTime;
private String accessToken;
}
For saving object to column with Hibernate, I use Hibernate Types project.
Now I want to get All LiveTokens that expired. I can't do it with Spring JPA. How do I query to posgresql jsonb column with Spring data JPA?
SQL JSON functions
If you want to call a SQL function that processes the JSON column, then you just need to make sure you register the function with Hibernate prior to using it.
JSON operators
For JSON operators, like the PostgreSQL ones (e.g., ->>), the only option you have is to use a native SQL query. JPQL or HQL don't support database-specific JSON operators.
Using EclipseLink and spring data jpa, if your data in db is something like: {"expireTime":102020230201, "accessToken":"SOMETHING" }, my first question is why to use long numbers for your dates instead of timestamps (ex '2019-09-14 12:05:00'). If you use timestamps there are also options to manage timezones (either from postgresql or from you source code).
Regarding your issue you may use the FUNC JPQL keyword of EclipseLink (Hibernate may have something similar) in order to run a database specific function. In the example below I use FUNC('jsonb_extract_path_text', lt.token, 'expireTime') to get the values of the json for token.expireTime.
PostgreSql method jsonb_extract_path_text returns text, thus you cannot do a less that condition, so I cast the output of the function using JPQL CAST keyword with (CAST -data- TO -type-).
#Repository
public interface MyRepository extends JpaRepository<LiveTokens, Integer> {
#Query(value = "SELECT lt FROM LiveTokens lt WHERE CAST(FUNC('jsonb_extract_path_text', lt.token, 'expireTime') AS LongType) < :expirirationThreshold")
List<LiveTokens> findByExpireTime(#Param("expirirationThreshold") Long expirirationThreshold);
}
Again, this is not tested.
This is how a native query using postgres JSON query operators would look like, incoorporating your example:
#Query(value="SELECT t.* FROM LiveTokens t WHERE CAST(t.token ->> 'expireTime' AS LONG) < now()", native=true)
Assuming your real tablename is LiveTokens, native queries do no longer use the JPA translations, and the tablename has to match the one in the DB. (You may also need to specify it's schema.)
Try it:
Service class:
Long currentTime = new Date().getTime();
Repository:
#Query("SELECT lt FROM LiveTokens lt WHERE lt.token.expireTime <= :currentTime")
List<LiveTokens> findExpiredLiveTokens(#Param("currentTime") long currentTime)

JPA: compare LocalDateTime attribute to builder.currentTimestamp()

Given:
#Entity
public class Paramter() {
#Id
private Long id;
private LocalDateTime startDate;
// getters & setters
}
// Extract from repository/dao method that grabs parameters:
...
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Parameter> query = builder.createQuery(Parameter.class);
final Root<Parameter> param = query.from(Parameter);
// The line below is generates following compilation error due to mismatch
// of the types of LocalDateTime and Timestamp parameters:
// java: no suitable method found for greaterThanOrEqualTo(javax.persistence.criteria.Path<java.time.LocalDateTime>,javax.persistence.criteria.Expression<java.sql.Timestamp>)
query.where(builder.greaterThanOrEqualTo(param.get(Parameter_.startDate), builder.currentTimestamp())); ...
How to properly use java 8 date-time api (more specifically LocalDateTime property) with jpa criteria builder to achive: "select * from parameter where start_date >= sysdate"?
Please consider:
The solution can be Oracle specific but should use JPA criteria builder
I do not want to create a select query to grab the sysdate and than use it in another select query to grab the valid parameters. It should be done in one query like in the expected query example above.
The time from the server is not reliable - database time should be used
Using Hibernate 5.2.16 which seems to include JPA 2.1. Changing to JPA 2.2 doesn't seem to make a difference.

Hibernate #Formula annotation causing sql exception errors when hibernate inserts table reference inappropriately

I'm trying to use a Hibernate #Formula to import some calculated information into one of my Entity classes. This is the code:
#Formula("(select max(site_visits.`date`) from site_visits where site_visits.siteId = id)")
#Temporal(TemporalType.TIMESTAMP)
private Date lastSiteVisit;
I've tried the query manually and it works fine. The problem is that when I request the object, the SQL hibernate generates gets mangled: it insists on inserting its own table reference into the middle of my column reference expressions, so I get the following SQL error:
Unknown column 'site_visits.site0_.date' in 'field list'
Any idea how to fix this?
Use Alias
#Formula("(select max(sv.date) from site_visits sv where sv.siteId = id)")
Also don't use ` in queries

Categories