SpringMVC, MySQL dynamic query - java

How I can make one dynamic query in Spring MVC with all my all my parameters from URL?
For exemple:/example?parameter1=test&parameter2=apple;fruit&parameter3=Park
For that link I want to have a query like that: SELECT * FROM news WHERE parameter1 = test AND parameter2 = apple AND fruits AND parameter3 = Park
In in the same time if I have a URL like that /example?parameter1=test&parameter2=apple
I don't want to create a new query in some one like SELECT * FROM news WHERE parameter1 = test AND parameter2 = apple;
More exactly, how can I create a dynamic query how change automatic in function of parameters from URL?

Its not good idea to pass dynamic data to query which will surely cause you SQL injection attacks.
You can use BeanPropRowMapper (http://www.mkyong.com/spring/spring-jdbctemplate-querying-examples/) and prepared statement kind of approach (Named parameters http://www.studytrails.com/frameworks/spring/spring-jdbc-template-named-params.jsp) in Spring JDBC to achieve similar but typed query. ( Your Java Bean getter and setter are automatically called, query is updated from that)
Don't write dynamic query ( keep it strict typed)

Check this if you can use it..
public StringBuilder prepareQuery(String tblName, String url)
{
StringBuilder query = new StringBuilder("Select * from " + tblName + " Where ");
String[] params = url.split("&");
for (int i = 0; i < params.length; i++ )
{
String str = params[i];
System.out.println(str.contains(";"));
if (i == 0)
{
query.append(str.replaceAll(";", " AND "));
}
else
{
query.append(" AND " + str.replaceAll(";", " AND "));
}
}
return query;
}

Related

How to create a dynamic WHERE clause for a query using an array list

I have a List<String> of categories and for each category, I want to add them to my WHERE clause by combining with AND operator like: SELECT question_id FROM question WHERE category = categ1 AND category = categ2 AND category = ...
Since the size of the categories list is changing, I cannot do something like this:
String sql = "SELECT question_id FROM question WHERE category = ? AND category = ?";
jdbcTemplate.query(sql, stringMapper, "categ1", "categ2");
How can I achieve what I want?
Either check if JDBC Template from Spring handle that for you using a syntax which could be something like (from the doc, I don't think it does)
SELECT question_id FROM question WHERE category in (?...)
Or write your own query with the problems that may arise:
List<Object> parameters = new ArrayList<>(categories.size());
StringBuilder sb = new StringBuilde("SELECT question_id FROM question WHERE 1=1");
if (!categories.isEmpty()) {
if (categories.size() == 1) {
sb.append(" and category = ?");
} else {
sb.append(" and category in ");
sb.append(categories.stream()
.map(ignored -> "?")
.collect(joining(", ", "(", ")")));
sb.append(")");
}
parameters.addAll(categories);
}
Object[] paramArray = parameters.toArray();
jdbcTemplate.query(sb.toString(), stringMapper, paramArray);
Notes:
some security/quality tool may report SQL issues because you are writing a dynamic SQL.
Oracle put a limit on 1000 elements per IN. You would have to partition categories per group of 1000 (or less).
I used a stream() in a more or less strange fashion in order to generate the "?". If you use commons-lang3, you can replace it by "(" + StringUtils.repeat("?", ", ", categories.size()) + ")" (the example in the javadoc was probably done with this kind of use).
if you only have category as single criteria, you may probably remove the 1=1 as well as the and.
I believe this may work for you:
// The SQL Query
String sql = "SELECT question_id FROM question";
// Create the WHERE clause based on the number of items in List...
StringBuilder whereClause = new StringBuilder(" WHERE ");
StringBuilder ps = new StringBuilder("");
for (int i = 0; i < categories.size(); i++) {
if (!ps.toString().isEmpty()) {
ps.append(" AND ");
}
ps.append("category = ?");
}
whereClause.append(ps.toString()).append(";");
//Append the WHERE clause string to the SQL query string
sql = sql + whereClause.toString();
//System.out.println(sql);
/* Convert the categories List to an Object[] Array so as to
pass in as varArgs to the jdbcTemplate.query() method. */
Object[] psArgs = categories.toArray(new Object[categories.size()]);
jdbcTemplate.query(sql, stringMapper, psArgs);

HQL query throws QueryException with working SQL query

I'm working with HSQLDB and Hibernate, and I want to perform search requests from my REST API.
For example, for a REST request like localhost:8080/search?token=a%20e, my method should create the following query: FROM Course WHERE Course.description LIKE '%a%' OR Course.description LIKE '%e%' and I get this exception:
javax.servlet.ServletException: java.lang.IllegalArgumentException: org.hibernate.QueryException: Unable to resolve path [Course.description], unexpected token [Course] [FROM model.Course WHERE Course.description LIKE '%a%' OR Course.description LIKE '%e%']
This is the code in SearchService's method for searching Course by description or name.
public List<Course> searchCourses(String token, MatchIn column) {
// Prepare and clean token, leaving only key words
String[] keyWords = token.split(" ");
// Build query and ask database to retrieve relevant courses
StringBuilder sb = new StringBuilder("FROM Course WHERE ");
String colName = "Course.";
if(column.equals(MatchIn.DESCRIPTION)) colName += "description";
else if(column.equals(MatchIn.NAME)) colName += "name";
sb.append(colName);
int i = 0;
sb.append(" LIKE \'");
sb.append("%");
sb.append(keyWords[i]);
sb.append("%\'");
if(keyWords.length != 1){
i++;
for (; i < keyWords.length; i++) {
sb.append(" OR " + colName +
" LIKE \'");
sb.append("%");
sb.append(keyWords[i]);
sb.append("%\'");
}
}
Query query = session.createQuery(sb.toString());
return query.list();
}
Note that in the exception I'm receiving, it says that my method is actually creating the following query: FROM *model.*Course WHERE Course.description LIKE '%a%' OR Course.description LIKE '%e%'
When I try SELECT * FROM Course WHERE c.description LIKE '%a%' OR c.DESCRIPTION LIKE '%e%'; in IDEA's SQL console, it runs successfully. (I'm not using SELECT * in the query I'm creating because HQL doesn't use it)
I'm new to HQL and SQL, so I don't know where the problem is.
EDIT:
In Debugger mode I found the exact place where the exception is being called. There seems to be a problem with Hibernate:
I don't know what's causing this issue.
HQL works with binding parameters, so adding directly LIKE '%key%' won't work. Hibernate will convert the HQL to a SQL so to achieve that you may do this:
for (; i < keyWords.length; i++) {
sb.append(" OR " + colName + " LIKE " + "key" + String.valueOf(i));// notice that I'm not adding the '%%'
}
then you have to bind the parameters:
Query query = session.createQuery(sb.toString());
for (int j = 0; j < keyWords.length; j++) {
query.setParameter("key" + String.valueOf(j), "%" + keyWords[j] + "%")
}
As you can see it's a lot of code for a simple query.
So basically you have 2 options:
Create a native SQL. session.createSQLQuery(...)
Use Criteria.
String colName = "";
if(column.equals(MatchIn.DESCRIPTION)) {
colName = "description";
} else if(column.equals(MatchIn.NAME)) {
colName = "name";
}
Criteria criteria = session.createCriteria(Course.class)
for(String key : keyWords) {
criteria.add(Restrictions.or(Restrictions.like( colName, "%" + key + "%"));
}
return criteria.list();
TIPS:
DO NOT CONCAT YOUR PARAMS. USE query.setParameter(..)

Hibernate search do a filter search on two different tables

here's my problem.
I have two indexed models ACL and Asset. There is no real link between these two tables instead of the ID of an ACL object is the same than the Asset object.
With HQL, I have this request how works perfectly:
String[] permissions
Query query = session.createQuery("From Asset as asset, "
+ "Acl as acl "
+ "where asset.id = :id and asset.id = acl.object.id "
+ AclService.buildQuery (permissions)
+ "and acl.group in (:groups) ");
query.setParameter("id", assetId);
query.setParameterList("groups", user.getGroup().getGroups().values());
public static String buildQuery(String[] perms) {
StringBuilder buf = new StringBuilder();
for (int i=0; i<perms.length; i++) {
buf.append("and ");
buf.append(perms[i]);
buf.append(" = true ");
}
return buf.toString();
}
I want to do the same database request, but by using Hibernate Search. How can I do this. I already looked this http://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#query-filter but I wasn't able to produce good results.
How can I do it exactly?
As Hardy is saying, you need a combined index of the two entities. Hibernate Search does not support (query time) index joining at this point.
Check out the section dealing with the indexing of associated entities in the reference guide. The #IndexEmbedded and ContainedIn annotations can be used for that.

Batch fetch in hibernate by changing only place holder

In Simple JDBC (with no Hibernate)
we can do batch select by changing only place holder, like this
PreparedStatement stmt = conn.prepareStatement(
"select id, name from users where id = ?");
for ( int i=0; i < 10; i++ ) {
stmt.setInt(i); // or whatever values you are trying to query by
// execute statement and get result
}
How we can do it in Hibernate?
Hope this helps you out,
String hql = "from Users s where s.id= :userId";
for(int i=0; i< 10;i++){
List result = session.createQuery(hql)
.setParameter("userId", i)
.list();
}
This is the most common and user friendly way. It use colon followed by a parameter name (:example) to define a named parameter
Example 1: Using setParameter() method
String hql = "from Student s where s.registerNumner = :registerNumner";
List result = session.createQuery(hql).setParameter("registerNumner", "12345").list();
The setParameter() method is smart enough to discover the parameter data type of bind variable.
Example 2: Using setString() method
You can use setString to tell Hibernate this parameter date type is String.
String hql = "from Student s where s.registerNumber = :registerNumber";
List result = session.createQuery(hql).setString("registerNumber", "12345").list();
Example 3: Using setProperties() method
This feature is great ! You can pass an object into the parameter binding. Hibernate will automatic check the object’s properties and match with the colon parameter.
Student student = new Student();
Student.setRegisterNumber("12345");
String hql = "from Strudent s where s.registerNumber = :registerNumber";
List result = session.createQuery(hql).setProperties(student).list();
Example 4:
You can use positional parameters also.
String hql = "from Student s where s.registerNumber = ? and s.studentName = ?";
List result = session.createQuery(hql).setString(0, "12345").setParameter(1, "Harshad").list();
But it’s vulnerable to easy breakage because every change of the position(i.e. index) of the bind parameters requires a change to the parameter binding code
Batch select:
You can use following way for batch select
String hql = "from Users s where s.id= :userId";
List finalResult = new ArrayList();
for(int i=0; i< 10;i++){
List result = session.createQuery(hql).setParameter("userId", i).list();
finalResult.addCollection(result );
}

Java/ JDBC – Multi Parameter Search Best Practice

I’m using the following code to generate a search results from a relational DB, depending on the multiple (Optional) search parameters from the web based client.
Presently I’m using “java.sql.Statement” to achieve the functionality but I need the same to be achieved using “java.sql.PreparedStatement” in order to prevent SQL injections.
Let me know a best practice to change the code
E.g.
User inputs from web based client.
param1 - Optional
param2 - Optional
dateParamFr - Optional
dateParamTo - Optional
Pseudo code of SQL patterns depending on the search parameters as follows
IF (WITHOUT ANY SEARCH PARAMETER){
SELECT * FROM TEST_TABLE;
}
ELSE IF(WITH param1){
SELECT * FROM TEST_TABLE WHERE COLUMN1= param1;
}
ELSE IF(WITH param1 & param2){
SELECT * FROM TEST_TABLE WHERE COLUMN1= param1 AND COLUMN2= param2
}
SO ON
………
Following is the fragment of Java code in my EJB
/*
NOTE : Hashtable pSearchParam is a method parameter
*/
Connection cnBOP = null;
Statement stmt = null;
StringBuffer sb = new StringBuffer("");
try {
cnBOP = jdbcBOP.getConnection(); // DataSource jdbcBOP
stmt = cnBOP.createStatement();
/* ######################## SQL BODY ######################################*/
sb.append("SELECT COLUMN1, COLUMN2, DATE_COLUMN ");
sb.append("FROM TEST_TABLE ");
/* ######################## SQL WHERE CLAUSE ##############################*/
if(pSearchParam.size()>=1){
sb.append("WHERE ");
Enumeration e = pSearchParam.keys();
int count =0;
while(e.hasMoreElements()){
if (count >=1) sb.append("AND ");
String sKey = (String) e.nextElement();
if (sKey.equals("param1")) sb.append ("COLUMN1 ='"+pSearchParam.get(sKey)+"' ");
else if (sKey.equals("param1")) sb.append ("COLUMN2 ='"+pSearchParam.get(sKey)+"' ");
else if (sKey.equals("dateParamFr")) sb.append ("DATE_COLUMN >= TO_DATE('"+pSearchParam.get(sKey)+" 00:00:00','DD/MM/YYYY HH24:MI:SS') ");
else if (sKey.equals("dateParamTo")) sb.append ("DATE_COLUMN <= TO_DATE('"+pSearchParam.get(sKey)+" 23:59:59','DD/MM/YYYY HH24:MI:SS') ");
count ++;
}
}
/* ######################## SQL ORDER BY CLAUSE ############################*/
sb.append("ORDER BY DATE_COLUMN DESC");
ResultSet rs = stmt.executeQuery(sb.toString());
Instead of
sb.append ("COLUMN1 ='"+pSearchParam.get("param1")+"' ");
You will have to do
sb.append ("COLUMN1 = ? ");
and then after you create the statement you do
stmt.setString(1, pSearchParam.get("param1"));
This is only for the first parameter, you need to do this for all statements and enumerate the index in
setString(int index, String param);
Note that you will need to use other methods for int, long, Date... etc
Depend on your database engine you may use SQL functions like
isnull(value,valueIfNull)
for example in MSSQL
select * from Order where storeId = isnull(?,storeId)
next in you java code
preparedStatement.setNull(1,java.sql.Types.INTEGER)
if you need omit this param from filter or,
preparedStatement.setInt(1,20)
if you need find all orders with storeId = 20
This really looks like a job for Hibernate Criteria Queries...
Criteria is a simplified API for retrieving entities by composing
Criterion objects. This is a very
convenient approach for functionality
like "search" screens where there is a
variable number of conditions to be
placed upon the result set.
Are you using Hibernate? Then you can use the criteria API. Else for non hibernate you can take a look at the SqlBuilder tool to generate SQLs based on conditions.
Also you should use markers "?" instead of actual values.
So this query should be like this.
SELECT * FROM TEST_TABLE WHERE COLUMN1= ?;
You can then use PreparedStatements to set values for this column. An introductory tutorial on using PreparedStatement is here.

Categories