Order by clause query in Spring MVC with MapSqlParameterSource - java

I am trying to a SQL query in Spring MVC 4 with order by clause. But it does not work. I am using MapSQLParameterSource class to define sql parameters.
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("lowerLimit", lowerLimit);
params.addValue("upperLimit", upperLimit);
params.addValue("filter", filter.trim()+"%");
params.addValue("order", order);
String sql = "SELECT * FROM tbl_Subject WHERE subjectName LIKE :filter ORDER BY :order limit :lowerLimit, :upperLimit";
It does not work actually. does not order any columns. When I try to hard coded it works fine as aspect.
String sql = "SELECT * FROM tbl_Subject WHERE subjectName LIKE :filter ORDER BY subjectId DESC limit :lowerLimit, :upperLimit";
How to I Order by clause query with MapSqlParameterSource.

The problem is in this line :
params.addValue("order", order);//consider this return col_name
This will be translate it to :
.. ORDER BY 'col_name' limit ..
^ ^
and this is a wrong syntax, logically after ORDER BY you have to pass a column name and not a value.
.. ORDER BY col_name limit ..
^^^^^^^^
Instead you have to pass it to the query directly but be carfull to pass the correct name, this can cause Syntax error or SQL Injection if you pass a wrong input

Related

dynamic Query in #Query annotation

I have situation in which I have to compose a JPQL query in some method, then pass this query to spring data query method to be used as the query in #Query annotation.
#Query(value = ":DyanamicQuery")
List<PrizeInsuranceConfiguration> filterConfigurPrizeInsurance(String DyanamicQuery);
This is not possible. As it throws compile error. Can you give me an alternat way to achieve this?
#Query(value = ":DyanamicQuery")
List<PrizeInsuranceConfiguration> filterConfigurPrizeInsurance(String DyanamicQuery);
expected to get Query that I made will go and sit at value = ":DyanamicQuery" I also tried giving #Query("#{#entityName}").
You can create a JPQL query as a string, but you cannot pass it as a parameter to the value attribute of the #Query annotation.
You can use below example -
String dyanamicQuery= "SELECT * FROM table_name WHERE condition= :condition";
#Query(value = dyanamicQuery)
List<Entity> findByField(#Param("condition") String condition);
Or you can use below example also -
#Query(value = "SELECT * FROM table_name WHERE condition= :condition")
List filterConfigurPrizeInsuranceByField(#Param("condition") String condition);

SQL Syntax error in Hibernate's map function

SELECT NEW Map (PRODUCT_CATEGORY, COUNT(PRODUCT_CATEGORY) AS COUNTER) from Product WHERE USER_ID = (SELECT USER_ID FROM USERS WHERE USERNAME='burak123'
Hi everyone,
As hibernates document says here: https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-select
I am trying to map the query result into a hash map like this
#Query(value = "SELECT NEW Map( PRODUCT_CATEGORY , COUNT(PRODUCT_CATEGORY) AS COUNTER ) from Product WHERE USER_ID=(SELECT USER_ID FROM USERS WHERE USERNAME=(:username)) ",nativeQuery = true)
HashMap<Integer,Integer> getCategoryCountsWithUsername(#Param("username")String username);
But it throws an JdbcSyntaxErrorException. I am trying to solve this for like 1 hours already. Can someone help?
You are using a native query, not an HQL query. Check your SQL syntax.
With a native query, your named parameter won't work. You need to remove the nativeQuery = true

Could not locate named parameter even though parameter exists without surrounding quotes

when i run below query in my springboot project
select * from activity.activity where data->'userinfo' #> '[{"name": :username}]'::jsonb
i get this error "Could not locate named parameter [username], expecting one of []".
The table has a data column with value as {"userinfo": [{"name": "john"},{"lastname":"cena"}]}
What could be the issue?
PS: i am executing the query using the entity manager to create a native query, and then calling query.getResultList().
CODE:
StringBuffer q = new StringBuffer("select * from activity.activity where data->'userinfo' #> '[{\"name\":").append(" :username").append("}]'::jsonb");
Query query = null;
query = em.createNativeQuery(q.toString(), Activity.class);
query.setParameter("username", getActivityDataRequest.getUserName());
return query.getResultList();
That is a string literal. It cannot contain a parameter. You do have a :jsonb parameter, though. You should use ANSI SQL casting to avoid that.
Instead you should change your query to
String sql = "select * from activity.activity where data->'userinfo' #> CAST(:uname as jsonb)"
// next line is psudocode
String value = JsonBuilder.newArray( JsonBuilder.newObject("name", username) ).toString();
// real code
return em.createNativeQuery(sql, ResultClass.class)
.setParameter("uname", value)
.getResultList();

JPA EclipseLink - Get multiple objects by primary key maintaining order

I'm using EclipseLink as JPA implementation and I need to get multiple objects using the primary key (numeric id). But I also need to maintain the given id order.
Using native mySQL this kind of behaviour can be obtained using ORDER BY FIELD
SELECT id FROM table WHERE id IN(9,5,2,6) ORDER BY FIELD(id,9,5,2,6);
I'm now trying to replicate this query using JPA implementation. As already established from this thread, the ORDER BY FIELD is not supported, so I went to a more low-level approach using a JPA native query.
I'm try to reach this goal using a parameter query, instead of using a raw statement. The first implementation was like this
Class<?> clazz = ...;
List<Long> ids = ...;
EntityManagerFactory emf = ...;
EntityManager em = emf.createEntityManager();
String statement = "SELECT * FROM table WHERE id IN (?)";
Query createNativeQuery = em.createNativeQuery(statement, clazz);
createNativeQuery.setParameter(1, ids);
List resultList = createNativeQuery.getResultList();
As you can see the ORDER clause is not there yet, for the first step I just trying to make the parameter query work using the ids list with the IN operator. In the setParameter method I tried to provide the List object, a comma separated list (as string) but none of them works. At the end they all finish with a sql syntax error.
I also tried to play with the parenthesis, with or without, but nothing works.
Here some test I made
String statement = "SELECT * FROM " + tableName + " WHERE id IN (?)";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.setParameter(1, ids);
The query does not give any error, but no results given.
String statement = "SELECT * FROM " + tableName + " WHERE id IN (?)";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.setParameter(1, Joiner.on(",").join(ids));
Only one result is given, but 7 ids was provided to the query
From this topic I also tried using ?1 instead of ?, but no changes. Is there a way to make the nativeQuery working with a list of ids?
For the moment I'm using the full raw SQL statement
String joinedId = Joiner.on(",").join(ids);
String statement = "SELECT * FROM " + tableName + " WHERE id IN (" + joinedId + ") ORDER BY FIELD(id," + joinedId + ")";
Query createNativeQuery = emJpa.createNativeQuery(statement, this.em.getClassObject());
createNativeQuery.getResultList();
But at first I started with the parameter query for optimization and performance related of parsing each time the statement.
EDIT
With the suggestion of Chris I tried a TypedQuery using the FUNCTION operator (which is available because I'm using the latest EclipseLink). Here is the resulting code
List<Long> ids = ...;
Class<?> clazz = ...;
String statement = "SELECT e FROM " + clazz.getSimpleName() + " e WHERE e.id IN (:idList) ORDER BY FUNCTION('FIELD', e.id, :idList)";
EntityManagerFactory emf = ...;
EntityManager em = emf.createEntityManager();
TypedQuery<?> query = em.createQuery(statement, clazz);
query.setParameter("idList", ids);
List resultList = query.getResultList();
And here is the error while executing this code
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Operand should contain 1 column(s)
Error Code: 1241
Call: SELECT ... all the fields ... FROM webcontent_type WHERE (ID IN ((?,?,?,?,?,?,?))) ORDER BY FIELD(ID, (?,?,?,?,?,?,?))
bind => [14 parameters bound]
Query: ReadAllQuery(referenceClass=WebContentType sql="SELECT ... all the fields ... FROM webcontent_type WHERE (ID IN (?)) ORDER BY FIELD(ID, ?)")
EDIT 2
Tried without the parenthesis but there is still an error
SELECT e FROM FrameWorkUser e WHERE e.id IN :idList ORDER BY FUNCTION('FIELD', e.id, :idList)
I must say that with a list of one element the code works, but with another list of 10 elements there is an error
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Operand should contain 1 column(s)
Error Code: 1241
Call: SELECT .... FROM webcontent_type WHERE (ID IN (?,?,?,?,?,?,?)) ORDER BY FIELD(ID, (?,?,?,?,?,?,?))
bind => [14 parameters bound]
Query: ReadAllQuery(referenceClass=WebContentType sql="SELECT .... FROM webcontent_type WHERE (ID IN ?) ORDER BY FIELD(ID, ?)")
at org.eclipse.persistence.internal.jpa.QueryImpl.getDetailedException(QueryImpl.java:382)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:260)
at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:473)
It seems that even w/o the parenthesis, the resulting statement has them
If you are going to use a native query, you must do it exactly like you would form the SQL for your database - this means you must break the list into its component parameters as JPA providers are not expected to change the SQL for you. Most providers handle lists in JPQL though, so "select e from Entity e where e.id in (:idList)" will work in EclipseLink.
Your missing bit is that 'FIELD' is not a JPQL construct. For this, you would have to use the JPQL 2.1 FUNCTION operator. Something like:
"Select e from Entity e where e.id in :idList order by FUNCTION('FIELD', e.id, :idList)"

resultSet obtained if parameter containing whitespace concatenated, but not using setString

I have this piece of code, with a prepared statement. I know the query is redundant. the parameter id is a string <space>413530 (" 413530"). Please note the preceding whitespace character.
String query = "SELECT RSCode as id FROM Customer WHERE RSCode=?";
PreparedStatement newPrepStatement = connection
.prepareStatement(query);
newPrepStatement.setString(1, id);
resultSet1 = newPrepStatement.executeQuery();
while (resultSet1.next()) {
System.out.println("Got a result set.");
logindata.add(resultSet1.getString("id"));
}
I do not get any results after executing this query.
Now, if I use the same statements and append the parameter as part of the string as follows:
String query = "SELECT RSCode as id FROM Customer WHERE RSCode=" + id;
PreparedStatement newPrepStatement = connection
.prepareStatement(query);
resultSet1 = newPrepStatement.executeQuery();
while (resultSet1.next()) {
System.out.println("Got a result set.");
logindata.add(resultSet1.getString("id"));
}
I get a result as after executing this prepared statement. Same also works with a java.sql.statement
I wish to know why the driver ignores the whitespace in the second piece of code, but has a problem in the first part.
If you use setString the parameter will be bound as a string resulting in this SQL (considering the bound parameter an SQL string):
SELECT RSCode as id FROM Customer WHERE RSCode=' 0123';
If you use concatenation the SQL used will be (considering the concatenated value as an integer, since space will be ignored as part of the SQL syntax):
SELECT RSCode as id FROM Customer WHERE RSCode=<space>0123;
In this case I would advise to convert it to int or long or whatever it is and bind it with the right type. With setInt() or setLong().
And if you field is a string you could normalize it first using for example:
String normalizedValue = String.trim(value);
newPrepStatement.setString(1, normalizedValue);
or even direct in SQL like:
SELECT RSCode as id FROM Customer WHERE RSCode=TRIM(?);
In scenario - 1, the query will look like this
"SELECT RSCode as id FROM Customer WHERE RSCode=' 413530'"
In scenario - 2, the query will look like this
"SELECT RSCode as id FROM Customer WHERE RSCode= 413530"

Categories

Resources