I have a form where user can select search criteria.
The criterias are say:
Product Name: Input field
Name Option: Radio button group - begins with (default selected)/ is/ contains
Country: dropdown of country
Status: All, Active, Blocked
Type: All, One, Two, Three
Only Product Name is mandatory. Other dropdowns are optional.
So if country is not given, I should find products for all countries.
If active is not given, I should find both active and blocked products.
If Type is not given, I should return all the three types products.
I am building hibernate query as below:
String productName = searchCriteria.getValue("productName");
String productNameCriteria = searchCriteria.getValue("productNameCriteria");
String country = searchCriteria.getValue("country");
String status = searchCriteria.getValue("status");
String type = searchCriteria.getValue("type");
Query prodQuery = null;
String prodSql = "select count(*) from Product p where";
// is
if (productNameCriteria.equalsIgnoreCase("IS")){
prodSql += "p.productName = '"+productName+"'";
}
// begins with
else if (productNameCriteria.equalsIgnoreCase("BEGINS WITH")){
prodSql += "p.productName = '"+productName+"%'";
}
// contains
else (productNameCriteria.equalsIgnoreCase("BEGINS WITH")){
prodSql += "p.productName = '%"+productName+"%'";
}
if(!country.equalsIgnoreCase("0")){
prodSql += " and p.country = '"+country+"'";
}
if(!status.equalsIgnoreCase("ALL")){
if(status.equalsIgnoreCase("active"))
prodSql += " and p.status = 'active'";
else
prodSql += " and p.status = 'blocked'";
}
if(!type.equalsIgnoreCase("ALL")){
if(type.equalsIgnoreCase("one"))
prodSql += " and p.type = 'one'";
else if(type.equalsIgnoreCase("two"))
prodSql += " and p.type = 'two'";
else
prodSql += " and p.type = 'three'";
}
prodQuery = this.em.createQuery(prodSql);
List<Object[]> results = prodQuery.getResultList();
Am I doing query building the right way ? Or is there any other efficient method ???
Thanks for reading!!
Try looking at Criteria Query
Criteria crit = sess.createCriteria(Product.class);
if (productNameCriteria.equalsIgnoreCase("IS"))
crit.add( Restrictions.eq("productName", productName);
else if (productNameCriteria.equalsIgnoreCase("BEGINS WITH"))
crit.add( Restrictions.like("productName", productName + "%")
// etc
If you absolutely must build a string query then you should be using a StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("select count(*) from Product p where ");
if (productNameCriteria.equalsIgnoreCase("IS"))
sb.append("p.productName = '").append(productName).append("'");
// etc
String query = sb.toString();
Using a StringBuilder reduces the number of instances created at runtime.
You could also look into using query parameters, which would reduce some of the query complexity, though I don't know what the runtime query performance effects are.
"select count(*) from Product p where p.productName = :productName"
"select count(*) from Product p where p.productName = ?"
You can then use Query#setParameter (or one of the other variants like setString) to define the values in the query. This is also a much, much better way of building the query because it's going to automatically manage quoting and escaping of values you're receiving from the UI. Use query parameters and not string concatenation, regardless of how you build the query string.
Yes .It will work if you build the query dynamically in this way .But the code will become tedious and noisy as it involves string manipulating of the where-condition clause .
For this kind of query 's use case , which is a search that allows users to specify a range of different property values to be matched by the returned result set , using Query By Example(QBE) is more efficient and elegant.
The idea of QBE is that you provide an instance of the queried class with some properties initialized, and the query will returns the records with matching property values.
Reference
Example JavaDocs
YouTube Hibernate Tutorial - Projections and Query By Example
Related
I'm working on a small program that lists local train stops in a numbered list then asks for the user to type the number of the station that they wish to see the next arrival time for.
The problem I have is I don't think the MySQL query is correct to retrieve the arrival time. The list returns empty. Using jdbc previously, this query worked fine:
"SELECT arrival_time FROM stop_times WHERE stop_id = '"
+ myStation.getID()
+ "' AND arrival_time > time('now', 'localtime') ORDER BY arrival_time asc;";
And the current hibernate query:
public List<String> getArrivals() {
sessionFactoryBean.getCurrentSession().beginTransaction();
String sql = "SELECT arrival_time FROM stop_times WHERE stop_id = '"
+ myStation.getID()
+ "' AND arrival_time > time('now', 'localtime') ORDER BY arrival_time asc;";
Query query = sessionFactoryBean.getCurrentSession()
.createSQLQuery(sql)
.addEntity(Station.class);
List<String> arrivals = query.list();
sessionFactoryBean.getCurrentSession().getTransaction().commit();
return arrivals;
}
Called from this method and where I get IndexOutOfBoundsException:
public String getNextArrival(int user_input) {
getStationName(user_input);
List<String> arrivals1 = arrival.getArrivals();
System.out.println(arrivals1);
System.out.println(arrivals1.size());
String arrivalTime = arrivals1.get(user_input);
return convertTime(arrivalTime);
}
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0,
Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at com.moeller.code.Stops.getNextArrival(Stops.java:73)
Line 73 String arrivalTime = arrivals1.get(user_input);'
The DataBase is stored locally.
There are several problems with this.
First, the query does not use parameters, which means you will likely make this mistake elsewhere where it will be a danger. You have to pass on variables like this:
String sql = "SELECT arrival_time FROM stop_times WHERE stop_id = ?"
+ " AND arrival_time > time('now', 'localtime') ORDER BY arrival_time asc;";
Query query = sessionFactoryBean.getCurrentSession()
.createSQLQuery(sql)
.addEntity(Station.class);
query.setParameter(1, myStation.getID());
See the question mark? That is a positional parameter. You can also use named parameters.
String sql = "SELECT thing FROM table WHERE column1 LIKE :ptrn";
...
query.setParameter("ptrn", "%that%");
Notice how inside the query the parameter starts with :, but it does not when calling setParameter.
This way of safely inserting parameters is called using "Prepared Statements", or "Parameterized Queries". Find a quick tutorial on them, they are very important.
Secondly, in getNextArrival you forget to check if the list has that many elements.
if (arrivals1.size() <= user_input) {
return null;
}
Of course then you have to be careful when it returns a null to the function where it's used.
You are using a wrong method for the List.
when yo use List.get(param) param should be the position that you are looking for, no the userInput.
you need loop the list and compare each position of the list with the user input.
best Regards
I have query which filters items by certain conditions:
#NamedQueries({
#NamedQuery(
name = ITEM.FIND_ALL_PARAMS_BY_COMPANY_COUNTRY_GROUP,
query = "SELECT i FROM Item i where "
+ "((i.idCompany=:companyId AND i.idEMGroup=:groupId) "
+ "OR (i.idCompany=:companyId AND i.idEMCountry =:countryId AND i.idEMGroup is null) "
+ "OR (i.idCompany is null AND i.idEMCountry = :countryId AND i.idEMGroup is null)) "
+ "order by i.idEMCountry desc, i.idCompany desc, i.idEMGroup desc")
})
In some cases parameters idEMGroup o companyId can be null which generates sql looking like this IdEmCompany = 200630758) AND (IdEMGroup = NULL) and it is incorrect sql syntax is it possible to dynamically if value is null for it as 'Column IS NULL' instead of 'Column = NULL' without adding a lot of if's, or it's just better to rewrite this query using Criteria API and just check if value is present and add predicates on certain conditions ?
Correct answer would be to use CriteriaQuery.
Though it is also possible to construct the query dynamically but manipulating #NamedQuery is not possible or might require stuff that makes it not worth to do.
Instead you could construct the query first as a String and create TypedQuery by manipulating the query string
String strQuery = "SELECT i FROM Item i"; // .. + the rest of stuff
if(null==companyId) {
// add something like "companyId IS :companyId"
// ":companyId" coulöd also be NULL"
// but to enable using tq.setParameter("companyId", companyId)
// without checking if there is param "companyId" so there always will
} else {
// add something like "companyId=:companyId"
}
TypedQuery<Item> tq = entityManager.createQuery(strQuery, Item.class);
tq.setParameter("companyId", companyId);
There will be some IFs but so will be in CriteriaQuery construction also.
Currently my code is
int customerId = 4;
String sql = "select id from coupon as A join coupon_use "
+ "as B on A.id=B.coupon where B.customer=" + customerId
+ " and B.like_at is not null;";
RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<Coupon> query = Ebean.find(Coupon.class);
query.setRawSql(rawSql);
List<Coupon> list = query.findList();
return ok(Json.toJson(list));
How do I avoid writing manual sql query but still have the ORM generate that query and return me the result?
Ebean will add appropriate joins based on the paths/properties used in the where and order by etc.
where couponUse.likeAt is not null.
Assuming couponUse.likeAt is the correct expression path ... Ebean will add a join to support the expression automatically.
I am wondering how can I parse raw string coming from webapp Java UI upon user selection to SQL query String in Java !
For Example : On UI, I have 100 companies (and also a database having info about these companies) and if suppose user selected two companies from UI and clicked search button then It should return all information about these two companies comp1 and comp2 and relationships among these two companies and fetch following query from sql database :
The table CompanyData contains all general information about companies like ID, Name, CEO, Stablishment year, Awards won, global ranks, etc....
Table ComapanyDomain contains information about the domains, technologies and expertise of companies,
SELECT * FROM CompanyData
WHERE ID IN (SELECT ID FROM CompnayDomain
WHERE companyName = "comp1"
and ID IN ( SELECT ID FROM CompnayDomain
WHERE companyName = "comp2"))
the string to be parsed It would look like " Company comp1 and comp2 " in above scenario
Thank you
I would approach it like this (I didn't use your sql query because it didn't make any sense at all, it would return always an empty set unless 2 companies have the same ID)
String input = "Company comp1 and comp2";
String data = input.substring(8, input.length());
String [] companies = data.split("\\b and \\b");
String sql = "SELECT * FROM CompanyData WHERE ID IN (SELECT ID FROM CompanyDomain WHERE companyName IN (";
for(int i = 0; i < companies.length - 1; i++) {
sql += "?,"
}
sql += "?)";
for(int i = 0; i < companies.length; i++) {
// Use prepared statement and insert the values here
}
I wonder if there is a good solution to build a JPQL query (my query is too "expressive" and i cannot use Criteria) based on a filter.
Something like:
query = "Select from Ent"
if(parameter!=null){
query += "WHERE field=:parameter"
}
if(parameter2!=null) {
query += "WHERE field2=:parameter2"
}
But i would write WHERE twice!! and the casuistic explodes as the number of parameter increases. Because none or all could be null eventually.
Any hint to build these queries based on filters on a proper way?
select * from Ent
where (field1 = :parameter1 or :parameter1 is null)
and (field2 = :parameter2 or :parameter2 is null)
Why can't you use a criteria, like this.
Other options (less good imho):
Create two named queries one for each condition, then call the respective query.
Or build up a string and use a native query.
Oh, do you just mean the string formation(?) :
query = "Select from Ent where 1=1 "
if(parameter!=null){
query += " and field=:parameter"
}
if(parameter2!=null) {
query += " and field2=:parameter2"
}
(I think that string formation is ugly, but it seemed to be what was asked for)