So i have a project where we use springBoot and PostgreSQL 10 with PostGis and hibernate.spatial for spatial queries. Everything works fine so far.
A new requirement is to find entities, which start-end dates overlap the start-end dates of the query in any possible way (ranges might be enclosing, start-overlap, inbetween, end-overlap).
In PostgreSQL theres the Overlaps operator that seems a pretty good fit for the job.
When trying to use it within my JPA-Query for an Entity "Sth" like this..
select sth from Sth sth where 1=1 and (sth.start, sth.end) overlaps (:begin, :end)
// set begin and end params..
i get one of..
antlr.NoViableAltException: unexpected token: overlaps
antlr.NoViableAltException: unexpected AST node: (
org.postgresql.util.PSQLException: FEHLER: rt_raster_from_wkb: wkb size (5) < min size (61)
Is it possible to use overlaps for dates with JPA without writing a native query?
So seems like there are three things you need to do to make it work.
Its probably not possible to use overlaps as an operator, but luckily it seems it can also be used as a function: overlaps(start1, end1, start2, end2)
Overlaps is not mapped by any of the hibernate-core PostgreSQL[NN]Dialects. But it is mapped by hibernate-spatial PostgisPG[NN]Dialects, where it maps to the st_overlaps-function for spatial overlapping. So you need to use your own custom Dialect that registers the overlaps function with an alias like so:
public class PostgisDialect extends org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect {
public PostgisDialect() {
super();
registerFunction("dateoverlaps", new StandardSQLFunction("overlaps", StandardBasicTypes.BOOLEAN));
}
}
and specify it as your spring.jpa.properties.hibernate.dialect
(or spring.jpa.database-platform
🤷♂️).
= true
like this:select sth from Sth sth where 1=1 and dateoverlaps(sth.start, sth.end, :begin, :end) = true
You might addtionally enhance this by using the coalesce
function to handle null-values, i.e.
select sth from Sth sth where 1=1 and dateoverlaps(coalesce(sth.start, sth.plannedStart), coalesce(sth.end, '3999-01-01'), :begin, :end) = true
which uses plannedStart
when start is null and a long-in-the-future-date when end is null / open.