How to escape '_' and '%' wildcards in Spring Data JPQL LIKE query? - kotlin

I have the following method in Spring data repositiry:
#Query("""
select t from ToneEntity t
where (:title is null or (t.title like %:title%))
and (:albumId is null or (t.album.id = :albumId))
and (:artistId is null or (t.artist.id = :artistId))
and (:creatorId is null or (t.creator.id = :creatorId))
""")
fun findFilteredTones(
#Param("title") title: String?,
#Param("albumId") albumId: Long?,
#Param("artistId") artistId: Long?,
#Param("creatorId") creatorId: Long?,
pageable: Pageable
): List<ToneEntity>
When a title contains _ or % chars, Spring data passes them as wildcards but not as literals.
Example: I have a tone with the title 'bug_with_underscore' in the database. User on the web UI passes '' to find tones with '' literal in the title, but the actual result includes all tones.
How to set up automatic character escaping in LIKE queries?
Version of Spring Data JPA: 2.3.5
I found several possible solutions:
Use concat and replace SQL functions: https://stackoverflow.com/questions/30740432/escaping-values-in-spring-data-repository
Shield wildcard characters before passing them to the query: https://stackoverflow.com/questions/53558667/java-jpa-sql-accept-wrong-with-like
Is there any better solution in 2022? So far, I haven't found anything better than using aspects to escape wildcard characters manually. Because this problem occurs very often in our project

Related

How best to process dynamic query parameters using Java & Spring

The problem:
I have an api endpoint which handles multiple query parameters. It is implemented using Spring, and the query parameters are used to query data from a postgres database, which I query with a JDBC Template.
I am searching for a mature query builder technology to solve my problem.
Example:
A trivial query could look something like this:
api/book?name=LOTR&cover=hardback
The query parameters are added to a map, and a query string is build from the maps data:
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> entry.getKey() + "='" + entry.getValue() + "' AND ")
.collect(Collectors.joining());
Its not the most efficient, as I must always remove the trailing "AND" clause from the string, but it works.
However, if the query where to look something like
api/book?name=LOTR&name=Ulysses&cover=hardback
there is now the addition of an "OR" clause, which the above code would not handle. I can see myself quickly getting into the territory of with tedious string parsing to create SQL statements.
So now that I have presented my problem, I wonder if there is a technology I can use which handles this kind of problem nicely?
I would like to avoid the use of any ORM for this project, so Hibernate and MyBatis are out of the question. I have looked at some JOOQ examples, but they do not look compatible with JDBC Template.
For trivial implementations like your first case where you have to remove last AND after your query is built there is a simple hack - immediately after WHERE you add 1 = 1 and then for every WHERE predicate you add AND [COLUMN] = [VALUE].
Note: most databases optimise use of constants in WHERE clause before execution, so performance will not be an issue
/*
select <columns> from <tables> where 1 = 1
[dynamically built Where predicates will come here from following code]
*/
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> "AND " + entry.getKey() + "='" + entry.getValue() + "'")
.collect(Collectors.joining());
However for serious production implementations you may want to use frameworks like myBatis that gives you possibilities of templating a query and then passing parameters at runtime to build final queries.
You can find a good tutorial here.
/* An example */
<select id = "getName_Id_phone" parameterType = "Student" resultType = "Student">
SELECT * FROM STUDENT
<where>
<if test = "id != null">
id = #{id}
</if>
<if test = "name != null">
AND name LIKE #{name}
</if>
</where>
</select>
Came across here with the same question. Maybe you want to take a look at RSQL 1

Django Concat columns with integers and strings

I have run into an issue using attempting to add values from two different columns together in a query, namely that some of them contain numbers. This means that the built in Concat does not work as it requires strings or chars.
Considering how one can cast variables as other datatypes in SQL I don't see why I wouldn't be able to do that in Django.
cast(name as varchar(100))
I would assume that one would do it as follows in Django using the Concat function in combination with Cast.
queryset.annotate(new_col=Concat('existing_text_col', Cast('existing_integer_col', TextField())).get())
The above obviously does not work, so does anyone know how to actually do this?
The use case if anyone wonders are sending jenkins urls saved as fragments as a whole. So one url would be:
base_url: www.something.com/
url_fragment: name/
url_number: 123456
I ended up writing a serializer that inherits from the base serializer that contains the urls fragments and a lot of other things. In it I made a MethodField for my complete url and defined a getter function that loaded in the different fragments and added them together. I also redeclared the fragmented fields to None.
The code inside the new serializer is:
complete = serpy.MethodField("get_copmlete")
serverUrl = serpy.Field(attr=None, call=False, required=False)
jobName = serpy.Field(attr=None, call=False, required=False)
buildNumber = serpy.Field(attr=None, call=False, required=False)
def get_complete(self, obj):
return obj.server_url + obj.job_name + '/' + str(obj.build_number)

Apache Calcite query parser - Unexpected character double quotes

I am using Apache Calcite to execute queries on different data sources.
The model file that I am using is
inline: {
version: '1.0',
defaultSchema: 'sakila',
schemas: [
{
name: 'sakila',
type: 'custom',
factory: 'org.apache.calcite.adapter.jdbc.JdbcSchema$Factory',
operand: {
jdbcDriver: 'org.postgresql.Driver',
jdbcUrl: 'jdbc:postgresql://localhost:5432/sakila',
jdbcUser: 'postgres',
jdbcPassword: 'postgres'
}
}
]
}
And the query is
select
"sakila"."actor"."first_name" as "actor_first_name"
from
"sakila"."actor"
The above query is not working due to the double quotes applied for tables and columns. So, I had to remove the quotes and the following query works fine.
select
sakila.actor.first_name as actor_first_name
from
sakila.actor
Here, the question is the query parser is not allowing some queries if they don't have double quotes. And in some cases like above it is not requiring quotes to execute properly. Can anyone throw some insight on why exactly it is so?
I suspect that the cause is case-sensitivity. Assuming that Calcite is in its default lexical mode, if you remove the quotes around identifiers, Calcite will convert them to upper case before trying to find tables and columns with those names. You say that it works without quotes, so I presume that your schema, table and column are upper case (SAKILA, ACTOR, FIRST_NAME).
You can ask Calcite to be non-case-sensitive by passing caseSensitive=false as part of the Calcite JDBC connect string.

SQL wildcards via Ruby

I am trying to use a wildcard or regular expression to give some leeway with user input in retrieving information from a database in a simple library catalog program, written in Ruby.
The code in question (which currently works if there is an exact match):
puts "Enter the title of the book"
title = gets.chomp
book = $db.execute("SELECT * FROM books WHERE title LIKE ?", title).first
puts %Q{Title:#{book['title']}
Author:#{book['auth_first']} #{book['auth_last']}
Country:#{book['country']}}
I am using SQLite 3. In the SQLite terminal I can enter:
SELECT * FROM books WHERE title LIKE 'Moby%'
or
SELECT * FROM books WHERE title LIKE "Moby%"
and get (assuming there's a proper entry):
Title: Moby-Dick
Author: Herman Melville
Country: USA
I can't figure out any corresponding way of doing this in my Ruby program.
Is it not possible to use the SQL % wildcard character in this context? If so, do I need to use a Ruby regular expression here? What is a good way of handling this?
(Even putting the ? in single quotes ('?') will cause it to no longer work in the program.)
Any help is greatly appreciated.
(Note: I am essentially just trying to modify the sample code from chapter 9 of Beginning Ruby (Peter Cooper).)
The pattern you give to SQL's LIKE is just a string with optional pattern characters. That means that you can build the pattern in Ruby:
$db.execute("SELECT * FROM books WHERE title LIKE ?", "%#{title}%")
or do the string work in SQL:
$db.execute("SELECT * FROM books WHERE title LIKE '%' || ? || '%'", title)
Note that the case sensitivity of LIKE is database dependent but SQLite's is case insensitive so you don't have to worry about that until you try to switch database. Different databases have different ways of dealing with this, some have a case insensitive LIKE, some have a separate ILIKE case insensitive version of LIKE, and some make you normalize the case yourself.

Use of LIKE clause in sql prepared statement, spring, SimpleJDBCTemplate

I have the following sql prepared statement:
SELECT * FROM video WHERE video_name LIKE ?
Im using spring and jdbc.
i have a method, where term is a searchterm, sjt is a SimpleJdbcTemplate, VideoMapper is a RowMapper and searchForTermQuery is the string from above
...
return sjt.query(searchForTermQuery, new VideoMapper(), term);
My table has 2 videos that match the term.
However when I run the query none is found. I get an empty List.
I tried playing with % around the question mark, but it only gave badGrammarExceptions.
You need to put the % around the value itself, not around the placeholder (the question mark).
So:
return sjt.query(searchForTermQuery, new VideoMapper(), "%" + term + "%");