Testing: Incompatibility between MySQL and H2 - java

I'm trying to test my JPA native #Query with H2.
My native query is as follows:
#Query(
value = "SELECT * FROM accounts " +
" WHERE 'account_name' LIKE LOWER(CONCAT('%', COALESCE(:searchTerm, ''), '%')) ",
countQuery = "SELECT count(*) FROM accounts " +
" WHERE 'account_name' LIKE LOWER(CONCAT('%', COALESCE(:searchTerm, ''), '%')) ",
nativeQuery = true
)
When writing a unit test, I'm getting the following H2 error:
Caused by: org.h2.jdbc.JdbcSQLException: Table "ACCOUNTS" not found;
SQL statement: SELECT * FROM accounts WHERE 'aws_account_name' LIKE
LOWER(CONCAT('%', COALESCE(?, ''), '%')) limit ? [42102-197]
I can fix the H2 error by changing my SQL syntax to put table name in double-quotes:
#Query(
value = "SELECT * FROM \"accounts\" " +
" WHERE 'account_name' LIKE LOWER(CONCAT('%', COALESCE(:searchTerm, ''), '%')) ",
countQuery = "SELECT count(*) FROM \"accounts\" " +
" WHERE 'account_name' LIKE LOWER(CONCAT('%', COALESCE(:searchTerm, ''), '%')) ",
nativeQuery = true
)
HOWEVER, then my MySQL (actual non-test environment) complains:
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your
SQL syntax; check the manual that corresponds to your MySQL server
version for the right syntax to use near '"accounts" WHERE
'account_name' LIKE LOWER(CONCAT('%', COALESCE('ab', ''), ' at line 1
How can I test this native query with MySQL and H2?
The reason I'm using native query instead of JPQL is because I need to search without case sensitivity and allow "contains" matching.

By default, MySQL recognizes a token enclosed in double quotes as a string literal, not an identifier (i.e. a table name or column name).
If we modify MySQL sql_mode to include ANSI_QUOTES, then a token enclosed in double quotes will be seen as an identifier. But this is going to cause a problem in SQL where string literals were enclosed in double quotes rather than the SQL standard single quotes.
If H2 compatibility mode is set to MySQL, then we should be able to use the normal MySQL backtick characters to escape identifiers. e.g
SELECT *
FROM `accounts`
^ ^
Also, MySQL isn't going to object to this construct:
WHERE 'aws_account_name' LIKE
^ ^
but enclosed in single quotes, MySQL sees the token 'aws_account_name' here as a string literal, not as a reference to a column.
If that's supposed to be a column name, we can use the MySQL backtick characters around a column reference.
To reduce confusion and make it easier on a future reader, we typically like to qualify column references, even where it isn't strictly required. For example, using a short table alias:
SELECT t.*
FROM `accounts` t
WHERE t.`aws_account_name` LIKE ...

Related

MySQL Syntax Issue when using Accounts.UUID AND UUID =, with two tables

I'm having issues when using this:
WARN com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE Accounts.UUID = WarAccounts.UUIDAND Accounts.UUID = 'c7a00fe7-826d-46da-b4' at line 1
Here is the SQL:
SELECT
*
FROM
Accounts,
WarAccounts
WHERE
Accounts.UUID = WarAccounts.UUID
AND Accounts.UUID = ?
(Using prepared statements)
I'm very confused as to what is wrong with this to cause that issue, I've googled it but cannot find anything since this seems to be the correct way.
Server type: MySQL
Server version: 5.5.58-0ubuntu0.14.04.1 - (Ubuntu)
Wild ass guess:
In C# i would do this:
string query = "SELECT *" +
"FROM MYTABLE" +
"WHERE 1=1"
..to get your error..
Concat the string and you'll see the problem:
"SELECT *FROM MYTABLEWHERE 1=1"
The concatination should be:
string query = "SELECT * " +
"FROM MYTABLE " +
"WHERE 1=1 "
(see the spaces at the end of every line?)

How to use ':' in JPA native query?

I am doing some data analysis and I have a SQL query that uses SELECT ..., #rownum:=#rownum+1 as row_number, ... to get the row number along with the results. However when I put this into a native query on my JPA method (using Spring Boot / Spring Data JPA), I get the following exception:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: Space is not allowed after parameter prefix ':'
I have added #Query(nativeQuery = true, to my method, and have changed my parameters from things like where first_name = :firstname to where first_name = ?1 and removed the #Param annotation from my method parameters, but it hasn't helped.
Is there a way to escape the colon in this query, or some other way of assigning the rownum variable that doesn't use a colon?
To solve this issue you need to escape the two dots with double backslash \\:
"SELECT ..., #rownum\\:=#rownum+1 as row_number, ... "
^^^

Using datediff in sql jdbc query

I'm attempting to create a JDBC query with the following statement
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName +
"where datediff(d,DATECOLUMN2,getdate()) <= 1";
st = conn1.createStatement();
rs = st.executeQuery(query); //receiving error here
I am receiving the following error message
java.sql.SQLException: "d" is not a recognized table hints option. If it is intended as a parameter to a table-valued function or to the CHANGETABLE function, ensure that your database compatibility mode is set to 90.
I'm sure the query isn't recognizing the datediff function for some reason I am not sure why since i was previously using HQL in the same application and retrieving the values.
In an attempt to use an alternative function I used
{fn TIMESTAMPADD( SQL_TSI_DAY, 1, CURRENT_TIMESTAMP)}
but it also failed I later on found that this is only used for Derby Database's
Can someone assist me in using the proper sql function to compare a date with the current date using JDBC
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "+
"where datediff(day,DATECOLUMN2,getdate()) <= 1";
You have a comma before from. Based on the error messages you are running this against SQL server.
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "
+" where datediff(d,DATECOLUMN2,getdate()) <= 1";
The comma after the "d" should be a dot:
where datediff(d.DATECOLUMN2,getdate())
--------------- ^ dot here
The posted snippet doesn't have a closing double quote between tableName and +, but I figure that is just a typo. However, in your real code, where precisely is the double quote? Is it directly after tablename, like this
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName" +
or after the space that follows tablename, like this
String query = "SELECT COLUMN1,DATECOLUMN2 FROM tableName "+
It is very likely the former, because in that case the resulting query would look exactly the way as to cause the error you are getting. Take a look at this:
SELECT COLUMN1,DATECOLUMN2 FROM tableNamewhere datediff(d,DATECOLUMN2,getdate()) <= 1
You can see that where merges with the table name and datediff becomes an alias. What follows is interpreted as table hints. (You can specify table hints without WITH in older versions of SQL Server/older compatibility levels.) Consequently, SQL Server stumbles over d, as that is indeed an incorrect table hint.

How should I use COLLATE in Hibernate Query

I'am trying to use COLLATE statement in a Hibernate SQL query but, it doesn't recognize the statement.
CONSULTA: FROM Articulos WHERE activo=0
and (codigodearticulo like '%CIN EMB%' COLLATE='Modern_Spanish_CI_AI'
or descripcion like '%CIN EMB%' COLLATE='Modern_Spanish_CI_AI'
or descripcionadicional like '%CIN EMB%' COLLATE='Modern_Spanish_CI_AI' )
and codigodelinea in
(select CODIGODELINEA from Lineas where CATAUTOPARTES='1')
And when app compile, Hibernate return this exception :
- line 1:107: unexpected token: COLLATE
- line 1:107: unexpected token: COLLATE
- line 1:107: unexpected token: COLLATE
- Error hibernate: unexpected token: COLLATE near line 1, column 107
I can't find the problem, in MSSQL Server it works fine.
Unfortunately HQL isn't a complete replacement for SQL, and doesn't seem to allow specifying Collation at all.
It can be specified on a Criteria query. See this answer
Instead of having in HQL:
String hql = "SELECT e" +
" FROM EntityJPA e" +
" WHERE e.mycolumn COLLATE 'utf8_bin' = 'VALUE'"
Query query = entityManager.createQuery(hql);
You can use a Native Query like this
String sql = "SELECT t" +
" FROM entity_table t" +
" WHERE t.mycolumn COLLATE 'utf8_bin' = 'VALUE'"
and execute it a a Native Query:
Query query = entityManager.createNativeQuery(sql);
This was the simpler solution by keeping in mind that native query create a dependency on the SQL language of the target database (mySQL in our case).
For us this was still acceptable as the possibility to use a different DB Engine is very low.

Cannot use a LIKE query in a JDBC PreparedStatement?

The query code and query:
ps = conn.prepareStatement("select instance_id, ? from eam_measurement where resource_id in (select RESOURCE_ID from eam_res_grp_res_map where resource_group_id = ?) and DSN like '?' order by 2");
ps.setString(1,"SUBSTR(DSN,27,16)");
ps.setInt(2,defaultWasGroup);
ps.setString(3,"%Module=jvmRuntimeModule:freeMemory%");
rs = ps.executeQuery();
while (rs.next()) { bla blah blah blah ...
Returns an empty ResultSet.
Through basic debugging I have found its the third bind that is the problem i.e.
DSN like '?'
I have tried all kinds of variations, the most sensible of which seemed to be using:
DSN like concat('%',?,'%')
but that does not work as I am missing the ' on either side of the concatenated string so I try:
DSN like ' concat('%',Module=P_STAG_JDBC01:poolSize,'%') ' order by 2
but I just cannot seem to find a way to get them in that works.
What am I missing?
First, the PreparedStatement placeholders (those ? things) are for column values only, not for table names, column names, SQL functions/clauses, etcetera. Better use String#format() instead. Second, you should not quote the placeholders like '?', it would only malform the final query. The PreparedStatement setters already do the quoting (and escaping) job for you.
Here's the fixed SQL:
private static final String SQL = "select instance_id, %s from eam_measurement"
+ " where resource_id in (select RESOURCE_ID from eam_res_grp_res_map where"
+ " resource_group_id = ?) and DSN like ? order by 2");
Here is how to use it:
String sql = String.format(SQL, "SUBSTR(DSN,27,16)"); // This replaces the %s.
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, defaultWasGroup);
preparedStatement.setString(2, "%Module=jvmRuntimeModule:freeMemory%");
See also:
Sun JDBC tutorial: Using Prepared Statements
Format string syntax
If you want to use LIKE in prepared statement and also want to use % characters in LIKE;
write prepared statement as normally " .... LIKE ? ...." and while assigning parameter value to question mark use
ps.setString(1, "%" + "your string value" + "%");
This will work :)
There are two problems with your statement. You have to understand how bind variables work. The query is not processed by substituing the characters ? with your parameters. Instead, the statement is compiled with placeholders and then, during execution, the actual values of the parameters are given to the DB.
In other words, you parse the following query:
SELECT instance_id, :p1
FROM eam_measurement
WHERE resource_id IN (SELECT RESOURCE_ID
FROM eam_res_grp_res_map
WHERE resource_group_id = :p2)
AND DSN LIKE '?'
ORDER BY 2
I'm pretty sure the last parameter will be ignored because it is in a delimited character string. Even if it is not ignored, it does not make sense to have ' characters around because Oracle won't bind a parameter in a string (I'm surprised it hasn't raised any error, do you catch exceptions ?).
Now if you replace your DNS LIKE '?' with DSN LIKE ? and bind "%Module=jvmRuntimeModule:freeMemory%" this will make sense and should return the correct rows.
You still have the problem with your first parameter, it won't do what you expect, i-e the query that will be executed will be equivalent to the following query:
SELECT instance_id, 'SUBSTR(DSN,27,16)'
FROM ...
which is not at all the same as
SELECT instance_id, SUBSTR(DSN,27,16)
FROM ...
I would suggest parsing (=prepareStatement) the following query if you expect the SUBSTR to be dynamic:
SELECT instance_id, SUBSTR(DSN,?,?)
FROM eam_measurement
WHERE resource_id IN (SELECT RESOURCE_ID
FROM eam_res_grp_res_map
WHERE resource_group_id = ?)
AND DSN LIKE ?
ORDER BY 2
Omit the ' around the ?. Without the ', ? is a placeholder for a parameter. With it, it's an SQL string (i.e. the same as "?" in Java).
Then you must concatenate the string on the Java side; you can't pass SQL functions as parameters to queries; only basic values (like string, integer, etc) because the JDBC driver will convert the parameter to the SQL type the database expects and it cannot execute SQL functions in this step.
You can try:
String beforeAndAfter = "%" + yourVariable + "%";
PreparedStatement ps = con.prepareStatement(
"select columname from tablename where LOWER(columnname) LIKE LOWER('"+var+"%')");
Here var is the variable in which value that is to be searched is stored...
This should work:
"\'" + "?" + "\'"

Categories