4

how to select rows based on date range when sometimes the date range might have null values (select all rows)

I have a table called Project(id, name, certification_date); id is int, name is varchar(250) and certification_date is timestamp.

When a project is inserted then, only id and name is inserted, but certification_date is null. Later the user can certify the project then the row is updated with certification_date with the date the user certifies the project. So in project table i can have some rows with certification_date as null and some with a date (ex. 2020-06-18 00:12:07)

Now I have a search query with name and has a filter of startDate and endDate to filter certification_date. below is the Repository query with full signature.

@Query(value = "select distinct p.id, p.name, p.certification_date from project p WHERE (LOWER(p.name) LIKE CONCAT('%',LOWER(:searchKey),'%') and ((cast(:startDate as timestamp) is null or p.CERTIFICATION_DATE >= :startDate) AND (cast(:endDate as timestamp) is null or p.CERTIFICATION_DATE < :endDate ))",  nativeQuery = true)
public List<Object[]> searchProjects(@Param("searchKey") String searchKey, @Param("startDate") Date startDate, @Param("endDate") Date endDate);

So when searchProjects() executes with all the values, it returns the results correctly. But when i need no date filters, i.e startDate and endDate is null, it fails. (here we dont have any issue with searchKey because if searchKey is null I pass "" so it returns all values)

The Exception is

org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to timestamp without time zone

So what do i need to do to make the startDate and endDate optional, so that if those dates are not passed just return all(whether certification_date is null or has some values) rows ?

I have also tried many solutions from Stackoverflow but they don't work for me.

5 Answers 5

8
  1. The value of 'null' cannot be distinguished by JPA, especially for the mapping of 'date, timestamp, timestamptz' types in PostgreSQL.
  2. My solusion as follows:
    @Query(value = "select distinct p.id, p.name, p.certification_date from project p " +
            "where LOWER(p.name) LIKE CONCAT('%',LOWER(:searchKey),'%') " +
            "and (cast(cast(:startDate as text) as timestamp) is null or p.CERTIFICATION_DATE >= :startDate) " +
            "and (cast(cast(:endDate as text) as timestamp) is null or p.CERTIFICATION_DATE < :endDate)",  nativeQuery = true)
    public List<Object[]> searchProjects(@Param("searchKey") String searchKey, @Param("startDate") Date startDate, @Param("endDate") Date endDate);

Sign up to request clarification or add additional context in comments.

Comments

0
class Employee {
 int id;
 Date birthDate;
}

List<Employee> findByBirthDateNotNullAndBirthDateLessThanEqualAndBirthDateGreaterThanEqual(Date startDate,Date endDate)

1 Comment

Hi @Santosh, thanks for your reply. But I need to have List<Object[]> as return type and @Query() to write the query as in actual scenario I have complex Join with 3 tables. Thats why I omitted the join part and just posted the problem I faced.
0

Add casting only when comparing to null and not when actual data comparison occurs.

@Query(value = "select distinct p.id, p.name, p.certification_date from project p WHERE (LOWER(p.name) LIKE CONCAT('%',LOWER(:searchKey),'%') and ((cast(:startDate as date) is null or p.CERTIFICATION_DATE >= :startDate) AND (cast(:endDate as date) is null or p.CERTIFICATION_DATE < :endDate ))",  nativeQuery = true)
public List<Object[]> searchProjects(@Param("searchKey") String searchKey, @Param("startDate") Date startDate, @Param("endDate") Date endDate);

Comments

0

I had the same problem as you when my query had a timestamp filter, and this is how I solved it. I think this problem occurs because when JPA converts variables with ':' in front to native query, they will turn into '?' . This leads to variables having the same name in the code, but when sending sql to the DB, they will become '?' independent .So in the or condition both sides will be performed.

@Query(value = "select * " +
        "from qrcode.printer " +
        "where (cast(:name as text) is null or name like concat('%', cast(:name as text), '%')) " +
        "  and (cast(:email as text) is null or email like concat('%', cast(:email as text), '%')) " +
        "  and (cast(:createdAtFrom as text)  is null or created_at >= cast(cast(:createdAtFrom as text) as timestamp)) " +
        "  and (cast(:createdAtTo as text)  is null or created_at <= cast(cast(:createdAtTo as text) as timestamp)) " +
        "  and (cast(:updatedAtFrom as text)  is null or updated_at >= cast(cast(:updatedAtFrom as text) as timestamp)) " +
        "  and (cast(:updatedAtTo as text)  is null or updated_at <= cast(cast(:updatedAtTo as text) as timestamp)) " +
        "  and deleted = false " +
        "order by id desc", nativeQuery = true)
Page<Printer> findThenPaging(String name, String email, Date createdAtFrom, Date createdAtTo, Date updatedAtFrom, Date updatedAtTo, Pageable pageable);

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

With Spring you can also do it like this.

val rowParams = MapSqlParameterSource();
// `java.sql.Types.OTHER` indicates JDBC driver (and hence PostgreSQL) should handle the type conversion from a string to JSONB automatically.
rowParams.addValue("internalRawJson", dto.internalRawJson, Types.OTHER);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.