How to compare LocalDateTime values in JPQL query? - java

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);
}

Related

No Value specified for parameter 2

I am getting this error when trying to run my Spring boot.
java.sql.SQLException: No value specified for parameter 2
My code is this:
public UserTemp findHistoryByID(Integer Patient_Number) {
String sql = "select Col1\n" +
"from (\n" +
" select Past_Diagnoses_1 as Col1\n" +
" from patienthistory\n" +
" where Patient_Number = ?\n" +
" union\n" +
" select Past_Diagnoses_2 as Col1\n" +
" from patienthistory" +
" where Patient_Number = ?" +
" ) as T;";
return jdbcTemplate.queryForObject(sql, new Object[]{Patient_Number}, (rs, rowNum) ->
new UserTemp(
rs.getString("Col1")
));
}
As in the comments, you are having 2 placeholders in the SQL query. So you have to pass patient_number 2 times.
Coming to your second question, it depends on your requirement.
If you need a single result, you need to fix it on the DB side as it's a data issue or the query used is not proper.
If more than one result is allowed, you can use jdbcTemplate.queryForList() instead of jdbcTemplate.queryForObject(). And change the return type of findHistoryByID() to List<Map<String,Object>> and all callers of this function.
Note: Here key for each Map in List is column names returned from DB.
More information on jdbcTemplate.queryForList() is in official documentation

Couchbase parameterized query with numeric values

I'm trying to add numeric values to parameterized AnalyticsQuery but keep getting errors when the query runs. The java creating the query looks like this:
private ParameterizedAnalyticsQuery aggregateQuery(String userId, Long from, Long to) {
return AnalyticsQuery.parameterized(
"select d.field1,"
+ " d.field2"
+ " from data d"
+ " where d.userId = $userId"
+ " and d.timestamp between $from and $to",
JsonObject.create()
.put("userId", userId)
.put("from", from)
.put("to", to)
);
}
When the query is run the following error is returned:
<< Encountered \"from\" at column 213. ","code":24000}]
If I change the query to the following then it works and returns rows:
return AnalyticsQuery.parameterized(
"select d.field1,"
+ " d.field2"
+ " from data d"
+ " where d.userId = $userId"
+ " and d.timestamp between " + from
+ " and " + to,
JsonObject.create()
.put("userId", userId)
);
Why is there a problem when the parameters are not Strings? Is there a way to use parameterized queries with numeric values?
FROM and TO are reserved keywords in N1QL for Analytics and therefore must be put in backquotes when used as parameter names:
... and d.timestamp between $`from` and $`to`
For a list of all reserved keywords please see:
https://docs.couchbase.com/server/current/analytics/appendix_1_keywords.html

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).

LIKE COALESCE in Spring JPA does not work

So my problem is the following:
I have a table I want a filter to apply to. This filter should call a query which finds based on the given information the reports.
This is how my query looks:
#Query("SELECT r FROM Report r WHERE r.importanceLevel = COALESCE(importance,'%')" +
"AND r.source = COALESCE(source,'%')" +
"AND r.resolvedStatus = COALESCE(resolvedStatus,'%')" +
"AND r.header LIKE + '%' COALESCE(query,'%') + '%'")
List<Report> getReportsByAppliedFilter(#Param("importance") int importance, #Param("source") String source,
#Param("resolvedStatus") int resolvedStatus, #Param("query") String query);
Problem is: IntelliJ does not like the following:
LIKE + '%' COALESCE(query,'%') + '%'
Error: expected, got +
Do you have any idea how to solve this otherwise?
Yeah, time to go to bed. You dont use + for concat , you use concat():
#Query(value = "SELECT r FROM Report r WHERE r.importanceLevel = COALESCE(importance,'%')" +
"AND r.source = COALESCE(source,'%')" +
"AND r.resolvedStatus = COALESCE(resolvedStatus,'%')" +
"AND r.header LIKE CONCAT('%', COALESCE(query,'%'), '%')")
If your column name is request_status and table name is connections then use COALESCE like
#Query(value = "SELECT connections.id, COALESCE(connections.request_status,'') as request_status.... ", nativeQuery = true)
List<ZXYType> xysfunction();
Here if request_status is null then it will be replaced by an empty string.

MySQL - Optimizing my query and returning the more specific match

I posted a while back a question asking how to implement a search with the following criteria:
On the "Searching" page, the customer can search for movies by any of the following attributes or their combination (logical "AND" operation):
title;
year;
director;
star's first name and/or last name. This means you need to do both: a) first name or last name if only one of the two names is provided; b) first name and last name, if both names are provided.
I then have to spit back out information about that movie from my database given the search input. People suggested I use FullText Search, and I did. This is the solution I have come up with.
String searchInput = request.getParameter("searchInput");
String query = "";
// First see if there is a number in their search
// Note: doesn't consider more specific searches, because it only searches
// based on the given number
if (searchInput.matches("\\d+")) {
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(searchInput);
if (m.find()) {
String number = m.group();
int n = Integer.parseInt(number);
query = "SELECT title, year, director, id " +
"FROM movies " +
"WHERE year = " + n + " " +
"OR title LIKE '%" + n + "%'";
}
} else {
query = "SELECT title, year, director, r.id " +
"FROM movies AS r, stars AS s " +
"WHERE MATCH(title, director) AGAINST('" + searchInput + "*') " +
"OR MATCH(first_name, last_name) AGAINST('" + searchInput + "*') " +
"AND r.id IN (SELECT sim.movie_id " +
"FROM stars_in_movies AS sim " +
"WHERE sim.star_id = s.id " +
"AND sim.movie_id = r.id) " +
"GROUP BY title";
}
ResultSet rs = statement.executeQuery(query);
// Query again, this time ANDing the matches to see if
// there is a more specific search result
Statement statement2 = dbcon.createStatement();
query = "SELECT title, year, director, r.id " +
"FROM movies AS r, stars AS s " +
"WHERE MATCH(title, director) AGAINST('" + searchInput + "*') " +
"AND MATCH(first_name, last_name) AGAINST('" + searchInput + "*') " +
"AND r.id IN (SELECT sim.movie_id " +
"FROM stars_in_movies AS sim " +
"WHERE sim.star_id = s.id " +
"AND sim.movie_id = r.id) " +
"GROUP BY title";
ResultSet rs2 = statement2.executeQuery(query);
if (rs2.next()) {
// If there is a more specific match (ANDing the matches), use that instead
rs2.beforeFirst();
rs = rs2;
}
Now, I know this is REALLY bad code, since I'm executing almost the same query twice, with the only difference being that I AND the matches in the second query. I can't seem to figure out a way to combine the two queries or also reduce the amount of matching done to speed up the query. I also don't have a good way to deal with the year of the movie that the user may or may not input.
Here is what I have tried:
I have tried searching up the FullText Search documentation for some functions that can be included in my search to make my SQL query return the more specific result if it matches more things, but the only thing that comes closest to such a thing is the Boolean Full-Text Search. However, I don't think I can use such a function because I have to distinguish between the different user's keyword inputs.
I have also tried Googling AND/OR operations in MySQL that allows AND when possible, and if not possible, then ORs the query. However, it doesn't seem possible to do this.
UPDATE: I have nested subqueries now and the search is MUCH faster. However, I still need help with improving my code to return the more specific match and only the more specific match, if it exists. Else, return the more general match (if anything at all matches).
Run each sub query separately. You can AND and OR results in code as required, assuming the size of the results are reasonable. Bit more coding, but you only run the necessary queries once.

Categories

Resources