Check if java Timestamp is null in jpql query - java

I have the following code:
#Query("select t from Training t join t.skills s join t.trainers tr join t.discipline d where " +
"(t.name in :names or :names is null) and (s.name in :skills or :skills is null) and" +
" (t.location = :location or :location is null) and " +
" (d.name = :discipline or :discipline is null) and " +
"(tr.firstName in :trainers or :trainers is null) and " +
" (((:endDate > t.endDate) and (:startDate < t.startDate)) or (:startDate is empty))")
public List<Training> filterTrainings(List<String> names, List<String> skills, String location,String discipline,List<String> trainers,Timestamp endDate,Timestamp startDate);
and i need to check if :startDate and :endDate are null. Is there a way to do that?
The error i get is nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
when trying to check :startDate is null where start date is a Timestamp.

Could you try passing a LocalDateTime as a parameter instead of a Timestamp? java.sql.Timestamp might be causing you issues here. You can convert to a LocalDateTime by calling timestamp.toLocalDateTime()
Alternatively you could try passing the timestamp as a string into filterTrainings. If the timestamp is null before calling the filterTrainings method, assign an empty string. String _timestamp = timestamp == null ? "" : timestamp.toString()
Then, in your sql statement check if the string is empty .. ""=:timestamp OR function("to_timestamp", :timestamp, "yyyy-mm-dd hh:mm:ss.fffffffff"). The problem here is that we are using function to access native db commands.

Maybe you can build your SQL query with a and COALESCE(field, default_value_for_datetime_field). So you will not get NULL values.
You can build a default value as the start of the current month, of the current week, or any datetime value which could be a fair substitution for your model domain.

Related

How to compare LocalDateTime values in JPQL query?

It is necessary to select dataset using JPQL query with optional condition - comparing the field value (LocalDateTime type) with a user-specified parameter (also LocalDateTime type).
First I made a well working code:
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId
AND s.persistDate >= :userDateTime", SomeDTO.class)
.setParameter("userId", userId)
.setParameter("userDateTime", userDateTime)
This code works but there is one problem:
this condition may exist or may not exist - dependent on app logic. Therefore, there is a need not to use injection using .setParameter (for this condition), but to form a string (which may be empty) depending on the logic and then add to the request:
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= " + userDateString;
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId " + extraCondition, SomeDTO.class)
.setParameter("userId", userId)
But the problem is that no matter how I tried to format the userDateString variable, I get an Internal Server Error.I even tried using just a text string instead of variable (tried with different formatting):
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= 2023-01-27T23:30:50";
But the result is also bad - Internal Server Error.
I also tried using the .isAfter method instead of the ">=" operator, but that didn't help either.
How to inject LocalDateTime values comparing into query as String?
even if the date string may or may not be necesssary, you can (and should!) still use parameter injection, not formatted values.
Basically, your code should look like this:
String queryStr = ....;
boolean someCondition = <expensive_test_here>;
if(someCondition) {
queryStr += " AND s.persistDate >= :userDateTime";
}
Query q = em.createQuery(queryStr).setParameter("userId", userId);
if(someCondition) {
q.setParameter("userDateTime", userDateTime);
}

Pass a date as an argument

I did a SQL query in Java as follows:
"SELECT A.ID_MACHINE, A.HEURODATAGE, A.COMPTEUR, B.LIBELLE_IDMACHINE, C.LIBELLE_STATUT, C.CODE_COULEUR FROM ROXJAVA.MACH0004 A " +
"JOIN ROXJAVA.MACH0003 B ON A.ID_MACHINE = B.ID_MACHINE " +
"JOIN ROXJAVA.MACH0002 C ON B.CODE_MACHINE = C.CODE_MACHINE " +
"WHERE A.ID_MACHINE = ? AND A.HEURODATAGE BETWEEN '?' AND '?' AND A.CODE_STATUT = C.CODE_STATUT AND C.CODE_COULEUR = ? " +
"ORDER BY A.HEURODATAGE DESC";
In my WHERE it finds "Heurodatage" which must contain a time and a date with this format:
'2018-07-03 09:30:00.000'
I then want to retrieve the results of this query with the help of a method that takes into account the different attributes that I need to replace the? in my request.
But now I can not determine the type of my dates.
I'm getting "type not match" when I try to run with a String.
If the column is a date column, you want to pass in a date type:
PreparedStatement ps = ...;
ps.setDate(N, java.sql.Date.valueOf(your_date_value));
where it would be best if your_date_value is a java.time.LocalDate, but could also be parsed from a String (in a valid format).

MySQL DB or JDBC driver not handling LocalDate.MAX

I am trying to run a query with a start date and end date as parameters, which may be null.
MySQL - the DB or the JDBC driver or in combination - cannot handle null values by which I mean, the SQL AND endDate <= null excludes every row.
So I am trying to use startDate >= LocalDate.MIN which works well for the "no start date".
But endDate <= LocalDate.MAX doesn't work to allow any date. This is the SQL template:
String strStmt = "SELECT d.forecastId, d.valueDate, d.value " +
"FROM data d " +
"WHERE d.forecastId = ? " +
"AND valueDate >= ? " +
"AND valueDate <= ? " +
and this is the variable allocation:
if (startDate == null) {
statement.setObject(2, LocalDate.MIN);
} else {
statement.setObject(2, startDate.toInstant().atZone(
ZoneId.systemDefault()).toLocalDate());
}
if (endDate == null) {
statement.setObject(3, LocalDate.MAX);
} else {
statement.setObject(3, endDate.toInstant().atZone(
ZoneId.systemDefault()).toLocalDate());
}
try (ResultSet rs = statement.executeQuery()) {
...
and this is the SQL which it produces (via P6Spy):
SELECT valueDate FROM data d WHERE d.forecastId = 52010
AND valueDate >= '-999999999-01-01'
AND valueDate <= '+999999999-12-31'
This doesn't work, I get no rows.
If I then change it to something less extreme:
if (endDate == null) {
statement.setObject(3, LocalDate.parse("2019-01-01"));
...
I see the following in the P6Spy logging:
SELECT valueDate FROM data d WHERE d.forecastId = 52010
AND valueDate >= '-999999999-01-01'
AND valueDate <= '2019-01-01'
and the query works fine, except of course at some point in the future.
Is this some sort of overflow in MySQL? Should I use LocalDate.MAX differently or what can I use instead of LocalDate.MAX?
I don't wish to revert to
statement.setDate(3, new java.sql.Date(Long.MAX_VALUE);
which produces the following:
AND valueDate <= '17-Aug-94'
which doesn't work either - maybe a Y2K or Year 2038 bug? P6Spy is not helping by displaying it with its own unhelpful format.
Exceeding limits of the data type DATE in MySQL
The data type DATE in MySQL 5.7 is limited to a much smaller range of values than the java.time.LocalDate type. Quoting from that doc, (emphasis mine):
The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in 'YYYY-MM-DD' format. The supported range is '1000-01-01' to '9999-12-31'.
In contrast, java.time.LocalDate.MAX is the date +999999999-12-31. The year 999999999 is well beyond the year 9999.
If your goal is recording a particular date far in the future as a stand-in for an unknown or indeterminate date, choose one not quite so far out. Or use NULL, but neither Dr. Chris Date nor I recommend using NULL.
Date-time capabilities vary widely amongst various databases. The SQL standard barely touches on the subject. So you must carefully read the documentation of your particular database to learn the limits and the behavior of that product. Likewise, your JDBC driver.
Assuming you're not intending to filter out rows that have a null value in valueDate, it seems that you're trying to create a condition that always evaluates to true when either the start or end date in the query is null. I think it would be simpler to just omit the (redundant) comparison in the null end/start date case, by generating the SQL dynamically:
String strStmt = "SELECT d.forecastId, d.valueDate, d.value " +
"FROM data d " +
"WHERE d.forecastId = ? ";
if (startDate != null) {
strStmt += "AND valueDate >= ? ";
}
if (endDate != null) {
strStmt += "AND valueDate <= ?";
}
Then, after creating the statement, you would dynamically add the correct number of date parameters:
int index = 1;
statement.setInt(index++, forecastId);
if (startDate != null) {
statement.setObject(index++, startDate.toInstant().atZone(
ZoneId.systemDefault()).toLocalDate());
}
if (endDate != null) {
statement.setObject(index, endDate.toInstant().atZone(
ZoneId.systemDefault()).toLocalDate());
}
try (ResultSet rs = statement.executeQuery()) {
//...

The data types datetime and time are incompatible in the greater than or equal to operator

I have a variable type time in a column of a table of the database.
How can I compare this value in java with this field I mean can i use date, gregoriancalendar?
I've tried adn I still have this message, please can someone give me an advice
Date d2 = new Date(); // timestamp now
Calendar cal = Calendar.getInstance(); // get calendar instance
cal.setTime(d2); // set cal to date
cal.set(Calendar.HOUR_OF_DAY, 10); // set hour to midnight
cal.set(Calendar.MINUTE, 30); // set minute in hour
cal.set(Calendar.SECOND, 0); // set second in minute
cal.set(Calendar.MILLISECOND, 0); // set millis in second
Date d3 = cal.getTime();
#SuppressWarnings("unchecked")
List<Asistencia> list = (List<Asistencia>) sessionFactory
.getCurrentSession()
.createQuery(
"select new Asistencia( asis.idAsistencia,"
+ "asis.horaInicio, asis.horaFin) "
+ "from Asistencia asis "
+ "where :hour >= asis.horaInicio and :hour <= asis.horaFin")
.setParameter("hour", d3).list();
I also used between
where :hour between asis.horaInicio and asis.horaFin
and the mesage is the same:
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - The data types datetime and time are incompatible in the greater than or equal to operator.
The data types datetime and time are incompatible in the greater than or equal to operator.
Here the class Asistencia:
public class Asistencia implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private long idAsistencia;
private Date horaInicio;
private Date horaFin;
private int idAula;
private int idCurso;
private int idPeriodo;
private Date fecha;
public Asistencia (){
}
public Asistencia (long idAsistencia, Date horaInicio, Date horaFin){
this.idAsistencia
this.horaInicio = horaInicio;
this.horaFin = horaFin;
}
}
It seems the only problem was I'm using SQL Server 2008 and is necessary to put sendTimeAsDateTime=false in the connections properties.
Here a similar question.
comparing time in sql server through hibernate
I encountered this error using SpringBoot(v2.2.2) and MSSQL server (jdbc7.4.1) when calling a JpaRepository API passing null dates. This was first version Repository API
#Query(value = "SELECT t FROM MyEntity t WHERE "
+ " AND ( ?3 IS NULL OR ( ?3 IS NOT NULL AND t.fromDate<= ?3 ))"
+ " AND ( ?2 IS NULL OR ( ?2 IS NOT NULL AND t.toDate>= ?2 ))")
List<MyEntity> getMyEntity(LocalDate fromDate, LocalDate toDate);
When calling API with null value for input dates i got the exception:
The data types date and varbinary are incompatible in the less than or equal to operator
I solved with a CAST:
#Query(value = "SELECT t FROM MyEntity t WHERE "
+ " AND ( ?3 IS NULL OR ( ?3 IS NOT NULL AND t.fromDate<= CAST( ?3 AS date ) ))"
+ " AND ( ?2 IS NULL OR ( ?2 IS NOT NULL AND t.toDate>= CAST( ?2 AS date ) ))")
List<MyEntity> getMyEntity(LocalDate fromDate, LocalDate toDate);
Without setting that sendTimeAsDateTime property, which is not available for the SQL Server drivers older than 3.0 (and some of us are stuck with what we have, for reasons), you could try to use a String instead of a date. This worked for me using a PreparedStatement and I bet it would work in this scenario also. Change the last line of your first code block to:
.setParameter( "hour", new SimpleDateFormat("HH:mm:ss.SSS").format(d3) ).list();
I've encountered the same issue when querying data by entity property of type javax.time.LocalDate via spring-data-jpa and eclipselink . Connection setting sendTimeAsDateTime=false didn't help. This was fixed by adding spring-data-jpa converter <class>org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters$LocalDateConverter</class> into the persistence.xml file. Hope this helps.
I also have the same issue. My sql data type is Time(7) and each time I want to compare it via JPQL query, that error comes out. Connection string sendTimeAsDateTime=false didn't work. Adding <class>org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters$LocalDateConverter</class> into the persistence.xml also didn't work.
What I do is, store data Time(7) from sql into String, for example this is my table design on sql
StartTime time(7)
In my java class I store that field into String variable
#Entity
#Table(name = "tableSchedule")
public class TableSchedulue implements Serializable {
private static final long serialVersionUID = -9112498278775770919L;
....
#Column(name = "StartTime")
private String startTime;
.....
}
When I use JPQL query like this (in repository)
#Query(value = "SELECT a "
+ "FROM TableSchedule a "
+ "WHERE a.startTime <= ?1 ")
List<TableSchedulue > getTestData(String startTime);
Your string format must HH:mm:ss
It works. Hope it helps
CONS : Because in SQL the type is Time(7) so value 08:00:00 in SQL will become 08:00:00.0000000 in persistence so you need to parse it into your needs

Using a java.sql.Timestamp object in an sql query

I am trying to run a query in java that uses a java.sql.Timestamp object as the date to compare with in the where clause.
Here is how the query string that is built in Java
String getTransactionsSQL = "Select transaction_seq " +
"From transactions ct " +
"Where id = 'ABCD'" +
"And ct.out_msg_timestamp" +
"<= to_date('" + parseDate.getTimeStamp() +"','YYYY-MM-DD HH:MI:SS..')" +
"order by transaction_seq";
The statement parseDate.getTimeStamp() returns a java.sql.TimeStamp object that contains a date. Here is an example output of System.out.println(parseDate.getTimeStamp());
2011-03-07 05:47:57.0
When i run the above query i get this error
java.sql.SQLException: ORA-01843: not a valid month
Any clues?
Use PreparedStatement: http://download.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html
Never use string concatenation to pass arguements to SQL commands (security risk: SQL injection)!

Categories

Resources