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(..)
Related
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);
I am new to using SQL2O with MySQL, but I am having a weird problem, where different queries return same results. Is SQL2O returning me cached results?
My code looks like this:
String sql = "SELECT * " +
"FROM report_A" +
"ORDER BY :order :sequence "+
"LIMIT :from, :limit";
int limit = 5;
int startIndex = (page-1)*limit;
String sequence = "DESC";
try(Connection con = sql2o.open()) {
if(order.contains("-")){
order = order.replace("-", "");
sequence= " ASC";
}
Query query= con.createQuery(sql)
.addParameter("from", startIndex)
.addParameter("limit", limit)
.addParameter("order", order)
.addParameter("sequence", sequence);
List<ReportA> result = query.executeAndFetch(ReportA.class);
con.close();
The 4 parameters always change, but the output remains the same. I have verified the queries in mysql workbench, the data is different, but SQL2O returns me the same set of data. Am I missing something?
Your query is invalid. It wont compile and throw an Sql2oException on execution.
The problem is, basically, that you can use parameters only for values, not for table names, column names or other keywords like "ASC". Changing those would change the structure of the query.
It's possible to construct queries with variable structure by good old string concatenation, i.e.
String sql = "SELECT * " +
"FROM report_A" +
"ORDER BY " + order " " + SEQUENCE +
"LIMIT :from, :limit";
and then
query(sql)
.addParameter("from", from)
.addParameter("limit", limit)
.executeAndFetch(...)
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.
How I can make one dynamic query in Spring MVC with all my all my parameters from URL?
For exemple:/example?parameter1=test¶meter2=apple;fruit¶meter3=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¶meter2=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;
}
I am trying to make "filter" search for all questions in my database. Now I get a exception telling me that I can't compare enum values with string. Is it because I don't use the fully qualified package name of wher the enum type is declared? If so, is it better ways than hard-coding the package name?
Exception Description: Error compiling the query [SELECT q FROM
Question q WHERE q.status = 'APPROVED'], line 1, column 40: invalid
enum equal expression, cannot compare enum value of type
[app.utility.Status} with a non enum value of type
[java.lang.String].
public List<Question> all(Status status, ViewOption viewOption) {
String jpql = "SELECT q FROM Question q ";
boolean isWhereClauseAdded = false;
if (status != Status.ALL) {
if (!isWhereClauseAdded) {
jpql += "WHERE ";
}
jpql += "q.status = '" + status + "'";
}
if (viewOption != ViewOption.ALL) {
if (!isWhereClauseAdded) {
jpql += "WHERE ";
}
// Check if 'AND' operator is needed.
if (status != Status.ALL) {
jpql += " AND ";
}
switch (viewOption) {
case ONLY_IMAGES:
jpql += "q.image != ''";
break;
case NO_IMAGES:
jpql += "q.image = '' ";
break;
}
}
TypedQuery<Question> query = entityManager.createQuery(jpql,
Question.class);
return query.getResultList();
}
The right thing to do would be to use a query parameter:
String jpql = "select ... where q.status = :status";
Query query = em.createQuery(jpql).setParameter("status", status);
Rather than creating your query dynamically be concatenating query parts, you should also use the Criteria API, which has been designed with this goal in mind.
Can you try changing:
jpql += "q.status = '" + status + "'";
To:
jpql += "q.status = app.utility.Status." + status;