JPQL: How to rewrite postgres native query to JPQL query that uses filter keyword - postgresql

Im trying to avoid using native query. I have this query that uses the filter function, how could I rewrite this to not use that and work in regular jpql?
#Query(
"SELECT time_bucket(make_interval(:intervalType), d.time) as groupedDate, " +
"CAST(d.team_Id as varchar) as teamId, CAST(d.service_Id as varchar) as serviceId, CAST(d.work_id as varchar) as workId, " +
"ROUND(CAST(count(d.value) filter ( where d.type = 'A') AS numeric) /" +
" (CAST(count(d.value) filter ( where d.type = 'B') AS numeric)), 4) as total " +
"FROM datapoint d " +
"WHERE d.team_Id = :teamId and d.service_id in :serviceIds and d.work_id = :workspaceId and d.type in ('A', 'B') " +
"AND d.time > :startDate " +
"GROUP BY groupedDate, d.team_Id, d.service_Id, d.workspace_Id " +
"ORDER BY groupedDate DESC",
nativeQuery = true
)

in the FROM statement you have to use the DAO object instead of the table name

Related

Syntax error when querying results directly to a DTO

My native query -
interface PodcastRepository: JpaRepository<Podcast, Long> {
#Query(value = "SELECT new com.krtkush.sample.modules.podcast.models.PodcastDTO" +
"(p.id, p.author, p.title, p.description c.name, c2.name) " +
"AS sub_category_name FROM podcasts p " +
"LEFT JOIN categories c ON p.podcast_category_id = c.category_id " +
"LEFT JOIN categories c2 ON p.podcast_subcategory_id = c2.category_id " +
"WHERE p.podcast_owner = :ownerId", nativeQuery = true)
fun getPodcastsByOwner(#Param("ownerId")owner: Long): List<PodcastDTO>
}
However, when I execute the function I get the following error -
org.postgresql.util.PSQLException: ERROR: syntax error at or near "." Position: 15
position 15 is . after SELECT new com
I'm following this tutorial - https://smarterco.de/spring-data-jpa-query-result-to-dto/
The difference is that I'm using SQL rather than JPQL.

aggregate function as tuple argument postgres

I want to pass aggregate function like min, max etc as query parameter using Tuple.
Below is my query:
"select $5(CAST (vol AS FLOAT)) AS agg_v, "
+ "time_bucket_gapfill" + "(($1::text || ' minutes')::interval, t) AS time_function_minute, "
+ "tag_id from rtdata "
+ "where tag_id = any($2) and t > $3 and t < $4 "
+ "GROUP BY (tag_id, time_function_minute) ORDER BY time_function_minute"
But I'm getting following exception:
io.vertx.pgclient.PgException: syntax error at or near
"("
at io.vertx.pgclient.impl.codec.ErrorResponse.toException(ErrorResponse.java:29)
at io.vertx.pgclient.impl.codec.PrepareStatementCommandCodec.handleErrorResponse(PrepareStatementCommandCodec.java:62)
at io.vertx.pgclient.impl.codec.PgDecoder.decodeError(PgDecoder.java:233)
at io.vertx.pgclient.impl.codec.PgDecoder.decodeMessage(PgDecoder.java:122)
at io.vertx.pgclient.impl.codec.PgDecoder.channelRead(PgDecoder.java:102)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:745)
But If I replace $5 with hardcode aggregate function it works. How can I pass aggregate function dynamically in this scenario?
RxJava code Snippet:
return txBegin()
.flatMapObservable(tx ->
tx.rxPrepare(abovesql)
.flatMapObservable(pq -> {
return pq.createStream(50,
Tuple.of(
evalBucketInterval(req),
req.getTags().toArray(new Integer[0]),
parse(req.getStartDate()),
parse(req.getEndDate()),
parse(req.getAggFunc())))
.toObservable();
})
.doAfterTerminate(tx::commit))
.map(this::toFuncJson);
PostgreSQL allows to use parameters only as values and doesn't understand when you try to use parameters for function names, table names, etc. So you cannot pass aggregate name as a parameter.
I suggest to work around it in your application by concatenating the string value containing the aggregate function name. I guess it can be something like, but I am not sure about the exact syntax and what limitations of your environment are:
"select "+ my_agg_func_name +"(CAST (vol AS FLOAT)) AS agg_v, "
+ "time_bucket_gapfill" + "(($1::text || ' minutes')::interval, t) AS time_function_minute, "
+ "tag_id from rtdata "
+ "where tag_id = any($2) and t > $3 and t < $4 "
+ "GROUP BY (tag_id, time_function_minute) ORDER BY time_function_minute"

Transforming complex postgres query into JPQL for Spring Hibernate repository

I have the following Postgres SQL query, in order to get alerts with the latest date:
SELECT latest_alerts.subject_id,
latest_alerts.alertconfiguration_id_id,
latest_alerts.alert_level,
latest_alerts.maxdate
FROM (SELECT subject_id,
alertconfiguration_id_id,
alert_level,
Max(date) AS maxdate
FROM alert
WHERE subject_id IN ( 'da157532-8de5-4c0c-8608-d924e670d5db', '63b99886-77c8-4784-b8f0-7ff5310f1272' )
AND alertconfiguration_id_id IN (
'6feb6b8b-6b96-4d5d-ac58-713b3cd637a0'
)
GROUP BY subject_id,
alertconfiguration_id_id,
alert_level) AS latest_alerts
INNER JOIN alert
ON alert.date = latest_alerts.maxdate
AND alert.subject_id = latest_alerts.subject_id
AND alert.alertconfiguration_id_id =
latest_alerts.alertconfiguration_id_id
AND alert.alert_level IN ( 'WARNING' )
ORDER BY latest_alerts.maxdate DESC;
This runs well on the postgres database generated by Hibernate. Note the odd id_id construction is because of an embedded key.
But I'm struggling to transform this into a JPA/JPQL query that I can use in a Spring Boot application. So far I have this:
#Query("SELECT" +
" latest_alerts.subject," +
" latest_alerts.alertConfiguration," +
" latest_alerts.alertLevel," +
" latest_alerts.max_date" +
"FROM" +
" (SELECT" +
" subject," +
" alertConfiguration," +
" alertLevel," +
" MAX(date) AS max_date" +
" FROM" +
" alert" +
" WHERE" +
" subject.id IN (:subjectIds) AND alertConfiguration.id.id IN (:alertConfigurationIds)" +
" GROUP BY" +
" subject, alertConfiguration, alertLevel) AS latest_alerts" +
" INNER JOIN" +
" alert" +
" ON" +
" alert.date = latest_alerts.max_date" +
" AND alert.subject = latest_alerts.subject" +
" AND alert.alertConfiguration = latest_alerts.alertConfiguration" +
" AND alert.alertlevel IN (:alertLevels)" +
" ORDER BY latest_alerts.date DESC")
Page<Alert> findLatest(#Param("subjectIds") List<UUID> subjectIds,
#Param("alertConfigurationIds") List<UUID> alertConfigurationIds,
#Param("alertLevels") List<AlertLevel> alertLevels,
Pageable pageable);
But Hibernate doesn't understand what to do with this to the point where it actually throws a nullpointer while parsing this query.
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract org.springframework.data.domain.Page ournamespace.sense.repository.AlertRepository.findLatest(java.util.List,java.util.List,java.util.List,org.springframework.data.domain.Pageable)!
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:93)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:63)
... many more
Caused by: java.lang.NullPointerException
at org.hibernate.hql.internal.antlr.HqlBaseParser.identPrimary(HqlBaseParser.java:4355)
at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:993)
at org.hibernate.hql.internal.antlr.HqlBaseParser.atom(HqlBaseParser.java:3549)
Any idea if this kind of query is even possible? With a nullpointer thrown by Hibernate it's a bit hard to see what part of the query is the problem.
Cannot do it with Hibernate. From the documentation:
Note that HQL subqueries can occur only in the select or where
clauses.
But you can use native query:
#Query(
value = "SELECT latest_alerts.subject_id,
latest_alerts.alertconfiguration_id_id,
latest_alerts.alert_level,
latest_alerts.maxdate ...
FROM ...",
nativeQuery = true)
Page<Alert> findLatest(#Param("subjectIds") List<UUID> subjectIds,
#Param("alertConfigurationIds") List<UUID> alertConfigurationIds,
#Param("alertLevels") List<AlertLevel> alertLevels,
Pageable pageable);

Spring Data JPA JSONB Paramaterization

What is the correct syntax (JPA, Spring Data, or SpEL) to convert this query into a Spring Data Repository nativeQuery?
SELECT *
FROM mytable
WHERE f_jsonb_arr_lower(myjsonb -> 'myArray', 'subItem', 'email')
#> '"foo#foo.com"';
I want to use an input parameter instead of hard-coding "foo#foo.com".
My model: Postgres myTable with a JSONB column myJsonb:
{
"myArray": [
{
"subItem": {
"email": "bar#bar.com"
}
},
{
"subItem": {
"email": "foo#foo.com"
}
}
]
}
Index described here.
The hard-coded version works:
#Query(value =
"SELECT m.* " +
" FROM mytable AS m " +
" WHERE f_jsonb_arr_lower(myjsonb -> 'myArray' ,'subItem', 'email') " +
" #> '\"foo#foo.com\"' " +
" ORDER BY ?#{#pageable} ",
// Spring Data nativeQueries with Pageable require a separate countQuery:
countQuery =
"SELECT count(m.id) " +
" FROM mytable AS m " +
" WHERE f_jsonb_arr_lower(myjsonb -> 'myArray' ,'subItem', 'email') " +
" #> '\"foo#foo.com\"' ",
nativeQuery = true)
Page<MyTableEntity> findAllHardcodedPageable(Pageable pageable);
But trying to leverage the lowercaseEmailAddress parameter in a Spring Data repository nativeQuery does not work:
#Query(value =
"SELECT m.* " +
" FROM mytable AS m " +
" WHERE f_jsonb_arr_lower(myjsonb -> 'myArray' ,'subItem', 'email') " +
" #> '\"?{lowercaseEmailAddress}\"' " +
" ORDER BY ?#{#pageable} ",
countQuery =
"SELECT count(m.id) " +
" FROM mytable AS m " +
" WHERE f_jsonb_arr_lower(myjsonb -> 'myArray' ,'subItem', 'email') " +
" #> '\"?{lowercaseEmailAddress}\"' ",
nativeQuery = true)
Page<MyTableEntity> findAllByEmailPageable
(String lowercaseEmailAddress, Pageable pageable);
In my Postgres query logging, I can see that the lowercaseEmailAddress parameter is never set:
LOG: execute S_2: COMMIT
LOG: execute S_3: BEGIN
LOG: execute <unnamed>: SELECT count(m.id) FROM mytable
AS m WHERE f_jsonb_arr_lower(myjsonb -> 'myArray',
'subitem', 'email') #> '"?1"'
LOG: execute S_11: ROLLBACK
Found the answer:
1) Pass only a double-quoted String to the spring data repository method:
String emailAddressWithDoubleQuotes = String.format("\"%s\"",emailAddress);
result = repository.findAllByEmailPageable(emailAddressWithDoubleQuotes, pageRequest).getContent();
2) The Spring Repository #Query needs to have the SpEL expression in parenthesis and be casted to jsonb:
static final String FIND_ALL_BY_EMAIL_QUERY = " FROM mytable AS m " +
" WHERE f_jsonb_arr_lower(metadata -> 'myArray', 'subItem', 'email') " +
" #> ( ?#{#lowercaseEmailAddress} )\\:\\:jsonb";
#Query( // only use 'ORDER BY #pageableWithNativeSort' on 'value' query:
value = "SELECT m.* " + FIND_ALL_BY_EMAIL_QUERY + " ORDER BY ?#{#pageableWithNativeSort} ",
// Spring Data nativeQueries with Pageable require a separate 'countQuery':
countQuery = "SELECT count(m.id) " + FIND_ALL_BY_EMAIL_QUERY,
nativeQuery = true)
Page<OrderEntity> findAllBysubItemEmail(
#Param("lowercaseEmailAddress") String lowercaseEmailAddress,
#Param("pageableWithNativeSort") Pageable pageableWithNativeSort);

An identification variable must be provided for a range variable declaration

I'm trying to use this query in my jpa but it doesn't work:
List<Object[]> query = em.createQuery("SELECT Tstat.idStatistiques, TL.codeLieu, TL.materiel, TL.zone, sum(Tstat.colis) as colis, Tstat.defaut, sum(Tstat.nbreDefaut) as nbreDefaut,"
+ " sum(Tstat.nonLu) as nonLu, sum(Tstat.multiple) as multiple, sum(Tstat.nonRecu) as nonRecu, sum(Tstat.incoherent) as incoherent, sum(Tstat.requete) as requete , "
+ "sum(Tstat.tempsFonctionnement) as tempsFonctionnement, SUM(Tstat.tempsUtilisation) as tempsUtilisation, Tstat.modeFonctionnement FROM "
+ "( SELECT CURRENT_DATE as horodatage, St.idStatistiques, St.colis, St.defaut, St.nbreDefaut, St.nonLu, St.requete, St.multiple, St.nonRecu, St.incoherent, St.tempsFonctionnement, St.tempsUtilisation, St.modeFonctionnement FROM Statistique St )"
+ " UNION "
+ "(SELECT h.horodatage, h.idStatistiques, h.colis, h.defaut, h.nbreDefaut, h.nonLu, h.nonRecu, h.requete, h.multiple, h.incoherent, h.tempsFonctionnement, h.tempsUtilisation, h.modeFonctionnement FROM Statistiqueshisto h )"
+ " Tstat "
+ "LEFT JOIN (SELECT * FROM Lieux) as TL on Tstat.idStatistiques = TL.code_VI WHERE idStatistiques like :A ").setParameter("A", 0040+"%").getResultList();
This gives me error
The expression is invalid, which means it does not follow the JPQL
grammar.