JPA query to work with daterange of postgresql - spring

Actual DB table:
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
room | text | | |
during | daterange | | |
Working DB query:
select * from room_reservation where during && daterange('[2020-10-15,2020-11-14)');
Entity Mapping:
#Column(name = "during", columnDefinition = "daterange")
private Range<Date> during;
Jpa Repository:
#Query(value = "select r.room from Room_Occupancy r where r.during && daterange('[?1, ?2)')", nativeQuery = true)
List<Long> findOccupiedRooms(#Param("d1") Date d1, #Param("d2") Date d2);
Exception:
Caused by: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type date: "?1"
Position: 65
How can I write same query in JPA?

Here, '[?1, ?2)' inside string literal parameter can't be replaced. You can use || to concat as string where the parameters can be replaced.
#Query(value = "select r.room from Room_Occupancy r where r.during &&"
+ " daterange('[' || :d1 || ',' || :d2 || ')')", nativeQuery = true)
List<Long> findOccupiedRooms(#Param("d1") Date d1, #Param("d2") Date d2);

As it's a native query use ? as parameter placeholders:
#Query(value = "select r.room from Room_Occupancy r where r.during && daterange('[?, ?)')", nativeQuery = true)
List<Long> findOccupiedRooms(#Param("d1") Date d1, #Param("d2") Date d2);

Try this code:-
#Query(value = "select r.room from Room_Occupancy r where r.during >= ?1 and r.during < ?2", nativeQuery = true)
List<String> findOccupiedRooms(Date d1,Date d2); //d1 < d2

#Query(value = "select r.room from Room_Occupancy r where r.during && daterange(''||'[' || :d1 ||',' || :d2 ||')' || '')", nativeQuery = true)
List<Long> findOccupiedRooms(#Param("d1") Date d1, #Param("d2") Date d2);
This worked

Related

How Can I construct native query in spring boot with dynamic param as placeholder based on condition?

I am using MSSQL server as DB and the method in the repository layer of my spring boot project:
List<Object[]> SummaryData(#Param("startDate") LocalDateTime startDate,
#Param("endDate") LocalDateTime endDate, List<Integer> cNoList);
here cNoList may contain only 0 or some values.
i want when cNoList contains values such as (45,30,20 etc.) then the below query will execute:
SELECT 'CUSTOMER' as title_key, COUNT(*) as total_count FROM LOGIN_TABLE lg left join USER_TABLE ut on lg.u_id = ut.tid_no where lg.user_type = 'CUSTOMER' and lg.is_verified = 1 and
lg.sys_time BETWEEN ?1 AND ?2 AND cId in (?3)
when cNoList contains only 0 then the last part of the above query --> "AND cId in (?3)" will not execute just like below:
SELECT 'CUSTOMER' as title_key, COUNT(*) as total_count FROM LOGIN_TABLE lg left join USER_TABLE ut on lg.u_id = ut.tid_no where lg.user_type = 'CUSTOMER' and lg.is_verified = 1 and lg.sys_time BETWEEN ?1 AND ?2
i have made an attempt like below:
"DECLARE #part VARCHAR(100) = ''; \n" +
"DECLARE #region_no INT = 420;\n" +
"DECLARE #start Datetime = '2022-07-01 00:00:00';\n" +
"DECLARE #end Datetime = '2023-01-17 23:59:59';\n" +
"DECLARE #tp VARCHAR(100) = 'CUSTOMER';\n" +
"SET #part = (CASE WHEN #region_no != 0 THEN 'and region_no = ' + CAST(#region_no AS VARCHAR(100)) ELSE '' END);\n" +
"EXEC ('SELECT ''' + #tp + ''' as title_key, COUNT(*) as total_count FROM LOGIN_TABLE lg left join USER_TABLE ut on lg.u_id = ut.tid_no where lg.user_type = ''' + #tp + ''' and lg.is_verified = 1 and \n" +
"lg.sys_time BETWEEN\n" +
"CONVERT(DATETIME, ''' + #start + ''', 120) \n" +
"AND\n" +
"CONVERT(DATETIME, ''' + #end + ''', 120) ' + #part)", nativeQuery = true)
List<Object[]> SummaryData(#Param("startDate") LocalDateTime startDate,
#Param("endDate") LocalDateTime endDate, List<Integer> cNoList);
this query executes well with the static value #region_no, #start, #end but these values will be assigned dynamically.
issues I am facing :
i tried putting a placeholder "?3" but not working. how can I put a placeholder like "?3" instead of 420 in this line "DECLARE #region_no INT = 420" ?
(the value of #region_no needs to be something of type array of int but i guess it is of type only int here)
how can i use the placeholder for "DECLARE #start Datetime = '2022-07-01 00:00:00';\n", i want to use something like
"DECLARE #start Datetime = ?1;"
You need to use :param instead of ?1.
For example:
"DECLARE #part VARCHAR(100) = ''; \n" +
"DECLARE #region_no INT = :cNoList;\n" +
"DECLARE #start Datetime = :startDate;\n" +
"DECLARE #end Datetime = :endDate;\n" +
"DECLARE #tp VARCHAR(100) = 'CUSTOMER';\n" +
"SET #part = (CASE WHEN #region_no != 0 THEN 'and region_no = ' + CAST(#region_no AS VARCHAR(100)) ELSE '' END);\n" +
"EXEC ('SELECT ''' + #tp + ''' as title_key, COUNT(*) as total_count FROM LOGIN_TABLE lg left join USER_TABLE ut " +
"on lg.u_id = ut.tid_no where lg.user_type = ''' + #tp + ''' and lg.is_verified = 1 and \n" +
"lg.sys_time BETWEEN\n" +
"CONVERT(DATETIME, ''' + #start + ''', 120) \n" +
"AND\n" +
"CONVERT(DATETIME, ''' + #end + ''', 120) ' + #part)", nativeQuery = true)
List<Object[]> SummaryData(#Param("startDate") LocalDateTime startDate,
#Param("endDate") LocalDateTime endDate,
#Param("cNoList") List<Integer> cNoList);

How to create a query for a known value on a known field, with a known date between 2 unknown dates)

The task is to find a known value in the specified field. Condition: There is a system date or a date that I want to use. This date must be between 2 dates ( the fields where the dates are known), that is, you need to check all the dates in the database, + additional condition:
it is necessary that the found string had in itself a previously known field marked IS NULL
The code written in Sql, below working.
select b.* from nc_proc b where b.code_proc = 'S.2.2' and b.date_on >= '12-AUG-19' and b.d_off IS NULL and b.date_off > '12-AUG-19';
'12-AUG-19' is a variable that is sent to me(more precisely, I form it from the system time)
'S. 2. 2' is a variable that I get and which is the main search.
How to make a request ?
here is an example that tried to make
#Query(value = "SELECT b.* FROM nc_proc b WHERE b.code_proc = ?1 and b.date_on > ?2 and b.d_off IS NULL and b.date_off > ?2 ", nativeQuery = true)
List <NcProc> findByCodeProcList2 (String value, String systemDate);
This don't work.
Please tell me how to fix. And is it possible not to use the query manually, but to do with the built-in keywords spring data ? Then this option would be better.
Update
#Query(value = "SELECT b.* FROM nc_proc b WHERE b.code_proc = ?1 and ?2 b.date_on BETWEEN b.date_off and b.d_off IS NULL", nativeQuery = true)
List <NcProc> findByCodeProcList2 (String value, String systemDate);
Hibernate: SELECT b.* FROM nc_proc b WHERE b.code_proc = ? and ?
b.date_on BETWEEN b.date_off and b.d_off IS NULL
11:02:58:871 WARN http-nio-8080-exec-1
o.h.e.j.s.SqlExceptionHelper:137 - SQL Error: 920, SQLState: 42000
19-сентября-20 11:02:58:871 ERROR http-nio-8080-exec-1
o.h.e.j.s.SqlExceptionHelper:142 - ORA-00920: invalid relational operator
In addition, I was told that BETWEEN includes the left and right restriction, and it is necessary that the desired date was in the specified interval and is not equal to the left and right restriction.
Update
it's work
#Query(value = "select * from nc_proc where code_proc = ?1 " +
"and date_on <= TO_DATE(?2, 'yyyy-mm-dd HH24:MI:SS') " +
"and TO_DATE(?2, 'yyyy-mm-dd HH24:MI:SS ') < date_off " +
"and d_off IS NULL",
nativeQuery = true)
NcProc findByCodeProc(String codeProc, String systemDate);
I wanted not to use the native record in the future.
Who knows how to make an expression from the keywords spring-data ?
I can't help on the Spring side, but for your queries I think you want something like:
SELECT b.* FROM nc_proc b WHERE b.code_proc = ?1 AND b.date_on > ?2 AND b.date_off < ?2 AND b.d_off IS NULL
It's unclear from your question whether the dates go b.date_on > '12-AUG-19' > b.date_off or the other way around, b.date_on < '12-AUG-19' < b.date_off. So you might have to switch the > < operators.

Spring Data JPA QueryLimit

#Query(value = "SELECT e FROM user Order By e.id DESC LIMIT 0, :n ")
List findTopN(#Param("n") long n);
#Query(value = "SELECT * FROM event_tracker.user Order By user.id DESC LIMIT 0, :n",nativeQuery = true)
List findTopN(#Param("n") long n);

Pass date parameter to native query

A user can perform actions based on an occurrence value. When this value is equal to 'DAILY', I would like to retrieve all daily actions that have not been completed the last 24 hours.
The working SQL query:
SELECT distinct a.* FROM action as a LEFT OUTER JOIN history as h
ON a.id = h.action_id
AND h.user_id= <user> WHERE a.occurrence = 'DAILY' AND (h.id is NULL OR h.entry_date < TIMESTAMP 'yesterday')
The equivalent native query:
#Query(value =
"SELECT distinct a.* FROM action a "
+ "LEFT OUTER JOIN history h "
+ "ON a.id = h.action_id "
+ "AND h.user_id = :userId "
+ "WHERE a.occurrence='DAILY' AND (h.id IS NULL OR h.entry_date < :yesterday) ", nativeQuery = true)
public List<Action> findAllAvailableActions(#Param("userId") Long userId, #Param("yesterday") ZonedDateTime yesterday);
How it is called in my service :
ZonedDateTime today = ZonedDateTime.now(ZoneOffset.UTC);
ZonedDateTime yesterday = today.minus(1,ChronoUnit.DAYS);
Long userId = userDTO.getId();
List<Action> result = actionRepositoryCustom.findAllAvailableActions(userId, yesterday);
However, I do get the wrong results in my tests (actions that have already been completed are returned). I am afraid this is linked to the date parameter. The attribute entry_date is declared as ZoneDateTime in my entity. What am I doing wrong ?
hibernate : 5.2.4
You can't pass a ZonedDateTime into a native SQL query. You need to convert it to Calendar:
#Query(value =
"SELECT distinct a.* FROM action a "
+ "LEFT OUTER JOIN history h "
+ "ON a.id = h.action_id "
+ "AND h.user_id = :userId "
+ "WHERE a.occurrence='DAILY' AND (h.id IS NULL OR h.entry_date < :yesterday)", nativeQuery = true)
public List<Action> findAllAvailableActions(#Param("userId") Long userId, #Param("yesterday") Calendar yesterday);
And you can convert your ZonedDateTime this way:
public Calendar convertToDatabaseColumn(ZonedDateTime entityAttribute) {
if (entityAttribute == null) {
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(entityAttribute.toInstant().toEpochMilli());
calendar.setTimeZone(TimeZone.getTimeZone(entityAttribute.getZone()));
return calendar;
}
This approach is described here: link

jpql native query not setting parameter

#Repository
public interface GroupRepository extends JpaRepository<Group, String> {
//Other queries....
#Query(value = "with cte(group_id, parent_group_id, group_name) as( "
+ "select group_id, parent_group_id, group_name "
+ "from hea.hea_group "
+ "where group_id = ?1 "
+ "union all "
+ "select g.group_id, g.parent_group_id, g.group_name "
+ "from hea.hea_group g "
+ "inner join cte on cte.group_id = g.parent_group_id "
+ "where g.parent_group_id is not null "
+ ") select * from cte", nativeQuery = true)
List<Object> getChildGroups(String groupId);
}
Above is the query that I have written that should return the parent group and all of its children. The query does what it is suppose to do when I replace the ?1 with a hard coded group id value and change the method to have no parameters, but when I try to run it as above it returns nothing even though I'm passing in the exact same value that I was hard coding.
Below is the sql that is being generated by the query. When I replace the ? with a group id an run it on a test database it returns the results that it should.
with cte(group_id, parent_group_id, group_name) as( select
group_id,
parent_group_id,
group_name
from
hea.hea_group
where
group_id = ?
union
all select
g.group_id,
g.parent_group_id,
g.group_name
from
hea.hea_group g
inner join
cte
on cte.group_id = g.parent_group_id
where
g.parent_group_id is not null ) select
*
from
cte
The variables are zero based so ?0 is what you should use.

Resources