I have a screen where users can search a table based on 1 or more of its columns. I am passing the columns the users would like to filter by as a DTO to the backend.
There I am building a SQL to query by checking for the presence of these fields in the DTO...
Object transactionSearch (TransactionSearchDTO dto ){
StringBuilder transactionQuery = new StringBuilder()
transactionQuery.append("SELECT id, delivery_date, order_date, customer_name FROM transaction where 1=1 ")
if (dto.order_id) {
transactionQuery.append("and order_id = ${dto.order_id}")
}
if (delivery_date) {
transactionQuery.append("and order_id = ${delivery_date}")
}
if (customer_name) {
transactionQuery.append("and order_id = ${customer_name}")
}
}
What I am ending up with is a ton of if statements for each column.
Is there a way to achieve this without having an if block per filter?
I found some ideas here but I need a solution in Groovy or Java.
i suggest to use GString instead of StringBuilder to pass parameters correctly to groovy.sql.Sql
class DTO{
int id
String name
Date created
}
def a = new DTO(id:123, created:new Date())
GString f(DTO a){
GString q = GString.EMPTY + 'select ' + a.getProperties().collect{k,v-> k}.join(',')+' from T'
a.getProperties().each{k,v->
if(k!='class' && v!=null){
q = q + ( q.getValueCount()==0 ? ' WHERE ' : ' AND ' )
q = q + k + " = ${v}"
}
}
return q
}
def s = f(a)
println s
println s.getValues()
println s.getStrings()
Related
Question
With MySQL and using a JDBC template, is there a way to build parameters from Java Lists so that the SQL request matches a couple of values with couples of values in a given set?
Details
The values should match only if the couple of values is present in the list.
It should not match if one is present in a couple of values and the other in another couple further into the list of couples.
That is to say, given that JDBC parametrized query:
SELECT *
FROM TABLE_1
WHERE (COL_1, COL_2) IN (:valuesSet)
Caution: valuesSet is a set of couples
And that Java code:
public void daoMethod(List<MyObject> values1, List<MyObject> values2) {
String query = "";
query = "SELECT *\n" +
"FROM TABLE_1\n" +
"WHERE (COL_1, COL_2) IN (:valuesSet)";
MapSqlParameterSource parameters = new MapSqlParameterSource();
// Build valuesSet here
parameters.addValue("valuesSet", valuesSet);
namedParameterJdbcTemplate.query(query, parameters);
}
Is there an elegant way to build a JDBC template without having to "manually" create the string?
The inserted :valuesSet should be something like:
"(values1.get(0), values2.get(0)), (values1.get(1), values2.get(1)), ..."
But how should I build that string?
Current Track
Currently, my first draft solution is to build the string by Java code like this:
List<String> valuesSet = new ArrayList<String>();
for (int i = 0; i < values1.size(); i++) {
String value1 = StringEscapeUtils.escapeSql(values1.get(i).toString());
String value2 = StringEscapeUtils.escapeSql(values2.get(i).toString());
valuesSet.add("('" + value1 + "','" + value2 + "')");
}
But it keeps escaping the result list to make it a String and adds ' around it. Therefore, it's not working.
TL;DR
Input:
List<Object> objects
Output:
SELECT *
FROM TABLE_1
WHERE (COL_1, COL_2) IN (
('object_1_val1', 'object_1_val2'),
('object_2_val1', 'object_2_val2'),
('object_3_val1', 'object_3_val2'),
('object_4_val1', 'object_4_val2'),
...
)
Mean:
NamedParameterJdbcTemplate
Use every value separately.
List<String> valuesSet = new ArrayList<>();
StringBuilder sqlIn = new SqlBuilder();
for (int i = 0; i < values1.size(); i++) {
sqlIn.append("(?, ?),");
valuesSet.add(values1.get(i).toString());
valuesSet.add(values2.get(i).toString());
}
I found out a workaround using CONCAT():
SELECT *
FROM TABLE_1
WHERE CONCAT(COL_1, ',', COL_2) IN (:valuesSet);
That way, valuesSet can be a simple list of strings and is passed as the following:
List<String> valuesSet = new ArrayList<String>();
for (int i = 0; i < values1.size(); i++) {
String value1 = StringEscapeUtils.escapeSql(values1.get(i).toString());
String value2 = StringEscapeUtils.escapeSql(values2.get(i).toString());
valuesSet.add(value1 + "," + value2;
}
parameters.addValue("valuesSet", valuesSet);
Then, the executed query is something like:
SELECT *
FROM TABLE_1
WHERE CONCAT(COL_1, ',', COL_2) IN (
'object_1_val1,object_2_val1',
'object_1_val2,object_2_val2',
'object_1_val3,object_2_val3',
'object_1_val4,object_2_val4',
...
);
if my data structure is like this
parentA
-------parentAA
--------------parentAAA
---------------------childA
if i can get "childA.name" . how can i know all the parent name till the top level.
so it will be like this > parentA/parentAA/parentAAA/childA
what is the best way to do this ?
i'm working with SQLite and JAVA/android .. thanks in adv.
____________ EDIT
Okay guys, thanks for all of u . so i just make it by repeating "select query". BOTTOM-UP this is the method i create
public String getPath(int id, int type) {
StringBuilder pathBuilder = new StringBuilder();
String sql = null;
int parentId = 0;
if (id == 0) {
pathBuilder.insert(0, "/root/");
return pathBuilder.toString();
}
if (type == LayerManagementActivity.PARENT) {
do {
sql = "SELECT id, name, parent_id from parents_table where id="
+ id;
Cursor c = mDatabase.rawQuery(sql, null);
if (c.moveToFirst()) {
parentId = c.getInt(2);
id = c.getInt(0);
pathBuilder.insert(0, "/" + c.getString(1));
c.close();
}
id = parentId;
} while (parentId != 0);
pathBuilder.insert(0, "/root");
pathBuilder.append("/");
} else if (type == LayerManagementActivity.CHILD) {
sql = "SELECT id, name, folder_id FROM childs_table WHERE id=" + id;
Cursor c = mDatabase.rawQuery(sql, null);
if (c.moveToFirst()) {
pathBuilder.append(c.getString(1));
id = c.getInt(0);
int folderId = c.getInt(2);
String path = getPath(folderId, LayerManagementActivity.PARENT);
pathBuilder.insert(0, path);
}
c.close();
}
Log.d("crumb", pathBuilder.toString());
return pathBuilder.toString();
}
In this SQLite Release 3.8.3 On 2014-02-03 has been added support for CTEs. Here is documentation WITH clause
Example:
WITH RECURSIVE
cnt(x) AS (
SELECT 1
UNION ALL
SELECT x+1 FROM cnt
LIMIT 1000000
)
SELECT x FROM cnt;
I have a table called project with a column named rates.
The rates column is a string that holds a JSON array.
To split this string into rows that I can use in an IN statement to get rows from the related table, I use this for the IN part
WITH
split(s, p) AS (
SELECT substr(printf("%s%s", ss, ","), instr(ss, ",")+1), trim(substr(ss, 0, instr(ss, ","))) from ( select replace(replace(rates,"[",""), "]","") ss from project where rowid = 1)
UNION ALL
SELECT substr(s, instr(s, ",")+1), trim(substr(s, 0, instr(s, ","))) FROM split
where p!=""
)
select p from split where p!=""
You can use nested set model. Nested sets have big advantage that they can be implemented in most SQL engines using simple, non-recursive SQL queries.
SQLite doesn't support recursive CTEs (or CTEs at all for that matter),
there is no WITH in SQLite. Since you don't know how deep it goes, you can't use the standard JOIN trick to fake the recursive CTE. You have to do it the hard way and implement the recursion in your client code:
Grab the initial row and the sub-part IDs.
Grab the rows and sub-part IDs for the sub-parts.
Repeat until nothing comes back.
I need to write hibernate query (createSQLquery) in Java land
I have query but I bit lost how to create criteria for my query.
basically I have to transfer regular where clause to hibernate .add(Restriction)
for instance lets take:
where (name LIKE '%test%' or lastname LIKE '%test%') and (site_id = 2) and (date>=121212 and date <= 343343)
Query query = sess.createSQLQuery(sql).addScalar("ID", Hibernate.long);
query.add(Restrictions.or (
Restrictions.like("name", "%test%")
Restrictions.like("lastname", "Fritz%")
))
.add(Restrictions.eq("site_id", new Integer(2)))
.add(Restrictions.add(Restrictions.gr("date_start", 122122)
.add(Restrictions.le("date_end", 345433)));
I did not have chance to run it since do not have access to my environment now, just would love to figure out this.
Thanks
You have to do like this
Criteria criteria = session.createCriteria(YourPojo.class);
criteria.add(Restrictions.eq("id", id));
criteria .add(Restrictions.or (
Restrictions.like("name", "%test%"),
Restrictions.like("lastname", "Fritz%")));
criteria.add(Restrictions.eq("site_id", new Integer(2)))
criteria.add(Restrictions.add(Restrictions.gr("date_start", 122122)
.add(Restrictions.le("date_end", 345433)));
return (criteria.list());
okay, problem solved this way:
Query from XML - I put in where clause
<Applications>
<Application name="AlertData">
<Query name="alerts">
<SQL>
SELECT
mr.measurement_date as measurementDate,
........ // more data
FROM measurement m
LEFT JOIN tags ON mr.tag_id = tags.tag_id
.......... // more joins
WHERE
mr.site_id = :siteid
$searchConstraints$ // just a string
$dateStartFragment$ // just a string
</SQL>
</Query>
</Application>
</Applications>
Next Java code:
if (search != null && !search.isEmpty()) {
searchFragment = " AND (UPPER(field1) LIKE :search" +
" OR UPPER(field2) LIKE :search)";
}
sql = this.prepareQuery(sql, ImmutableMap.of(
"searchConstraints", searchFragment,
"dateStartFragment", dateStartFragment
));
Query query = this.sessionFactory.getCurrentSession()
.createSQLQuery(sql)
//more query here
if (search != null && !search.isEmpty()) {
query.setString("search", "%" + search.toUpperCase() + "%");
}
public String prepareQuery(String queryString, Map<String, String> templateVars) {
ST stringTemplater = new ST(queryString, '$', '$');
for (String key : templateVars.keySet()) {
stringTemplater.add(key, templateVars.get(key));
}
String renderedQuery = stringTemplater.render();
return renderedQuery;
}
I have several tables. I have a query also. My problem is to generate the SQL query dynamically using Java.
I have the following fields in a separate table:
Collumn name status
po_number, Y
unit_cost, Y
placed_date , Y
date_closed, Y
scheduled_arrival_date Y
date_closed Y
order_quantity Y
roll_number N
product_sku N
product_category_name N
rec_vendor_quantity Y
vendor_name Y
et_conversion_unit_quantity Y
from which i have to generate a query when the status is Y, the problem here is some time the above columns
The following query is the out put of the above :
here i have inculded all the columns but i have to exculde the column which has the status of N, please help me to construt the query using java.
select
pi.po_number,poi.unit_cost,pi.placed_date CreateDate,
case when isnull(pi.date_closed) then pi.scheduled_arrival_date
else pi.date_closed end as ReceviedDate,
poi.order_quantity,poi.roll_number,p.product_sku product_name,
pc.product_category_name,poi.rec_vendor_quantity,pv.vendor_name,p.et_conversion_unit_quantity,pi.note
from
purchase_order as pi,
purchase_order_inventory as poi,
product_vendors as pv,
products AS p,
product_categories AS pc
where
pi.purchase_order_id=poi.purchase_order_id and
pc.product_category_id=p.product_category_id and
poi.product_id = p.product_id and
poi.product_category_id=pc.product_category_id and
pi.vendor_id=pv.product_vendor_id and
( ( pi.date_closed >= '2012-01-01' and pi.date_closed <='2012-09-05 23:59:59' )
or ( pi.scheduled_arrival_date >= '2012-01-01' and pi.scheduled_arrival_date <='2012-09-05 23:59:59') ) and
pi.po_type=0
and pi.status_id = 0 and poi.transaction_type = 0
order by pi.po_number
UPDATE :
QUERY : STEP 1:
SELECT rcm.id,rcm.tablename,rcm.columnname,rcm.size,rcm.displayorder,rcm.isactive FROM report_customise_master rcm where rcm.tablename !='employee' and rcm.isactive='Y' order by rcm.displayorder;
STEP 2 :
Java method to construct the query :
public Map getComplexReportQuery() {
String query = "SELECT rcm.id,rcm.tablename,rcm.columnname,rcm.size,rcm.displayorder,rcm.isactive FROM report_customise_master rcm where rcm.tablename !='employee' and rcm.isactive='Y' order by rcm.displayorder;";
String tableName = "", from = "", select = "";
StringBuffer sb = new StringBuffer();
Map<String, List<String>> resultsMap = new LinkedHashMap<String, List<String>>();
Map<String, String> displayOrderMap = new LinkedHashMap<String, String>();
Map queryMap = new LinkedHashMap();
if (!query.isEmpty() || query.length() > 0) {
sb.append(query);
}
Connection connection = getConnection();
if (connection != null) {
try {
PreparedStatement reportQueryPS = connection.prepareStatement(sb.toString());
ResultSet reportQuery_rst = reportQueryPS.executeQuery();
List<String> tables = new ArrayList<String>();;
if (reportQuery_rst != null) {
StringBuilder selectQuery = new StringBuilder(" SELECT ");
StringBuilder fromQuery = new StringBuilder(" FROM ");
while (reportQuery_rst.next()) {
tableName = reportQuery_rst.getString("tablename");
List<String> columns = resultsMap.get(tableName);
if (columns == null) {
columns = new ArrayList<String>();
resultsMap.put(tableName, columns);
}
columns = resultsMap.get(tableName);
String columnName = reportQuery_rst.getString("columnname");
columns.add(columnName);
}
tableName = "";
for (Entry<String, List<String>> resultEntry : resultsMap.entrySet()) {
tableName = resultEntry.getKey();
List<String> columns = resultEntry.getValue();
int i = 0;
for (String column : columns) {
selectQuery.append(tableName + "." + column);
if (i != columns.size()) {
selectQuery.append(",");
} else {
selectQuery.append("");
}
i++;
}
if (!tables.contains(tableName)) {
tables.add(tableName);
}
}
//to remove comma at the end of line
select = selectQuery.toString().replaceAll(",$", "");
tableName = "";
int i = 0;
for (String table : tables) {
fromQuery.append(table);
fromQuery.append(" ");
fromQuery.append(table);
if (i != tables.size()) {
fromQuery.append(",");
} else {
fromQuery.append("");
}
i++;
}
from = fromQuery.toString().replaceAll(",$", "");
queryMap.put("query", select + from);
}
//from = from+"ORDER BY "+orderbyColumn+" "+sort+" ";
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
closeConnection(connection, null, null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
} else {
System.out.println("Connection not Established. Please Contact Vendor");
}
return queryMap;// return the map/ list which contains query and sory and display order
}
STEP 3 : Result query
{query= SELECT purchase_order.po_number,purchase_order.placed_date,purchase_order.date_closed,purchase_order.scheduled_arrival_date,purchase_order_inventory.unit_cost,purchase_order_inventory.order_quantity,purchase_order_inventory.roll_number,purchase_order_inventory.rec_vendor_quantity,products.product_sku,products.et_conversion_unit_quantity,product_categories.product_category_name ,product_vendors.vendor_name FROM purchase_order purchase_order,purchase_order_inventory purchase_order_inventory,products products,product_categories product_categories,product_vendors product_vendors}
but this not what i wanted, Please help me to construct the query i have given.
Two queries
You need to make two queries:
Query which fields are enabled
Build the second query string (the one you want to build dinamically)
It's this way because a SQL query has to tell which columns will be included before querying any data. In fact it will be used to build the internal DB query plan, it is, the way the DB motor will use to retrieve and organize the data you ask.
Query all columns
Is it necesary to query only that fields? Can't you query everything and use the relevant data?
Joins
Looking at the updated question I guess you need to dynamically add where conditions to join tables correctly. What I should do is have a reference telling me what coindition to add when a table is present.
There are at least two options:
Based on table pairs present (by example: "if A and B are present then add A.col1 = B.col2")
Based on tables present ("if B is present, then add A.col1 = B.col2; A should be present"
Based on your example I think the second option is more suitable (and easy to implement).
So I should have some static Map<String, JoinInfo> where JoinInfo has at least:
JoinInfo
+ conditionToAdd // by example "A.col1 = B.col2"
+ dependsOnTable // by example "A" to indicate that A must be present when B is present
So you can use:
that info to add tables that should be (by example: even if A has no selected cols, must be present to join with B)
include the conditionToAdd to the where clause
Anyway... I think you are getting into much trouble. Why so dynamic?
You have to approach the thing step by step.
Firstly you have to create a query that will return all rows that have status='Y'
Then you will put the COLUMN_NAME in a list of Strings.
List<String> list = new List<String>();
while(rs.next()){
list.add(rs.getString(columnNumber));
}
And then you have to loop the List and generate dynamically your second sql statement
String sqlSelect = "SELECT ";
String sqlFrom = " FROM SOME_OTHER_TABLE "
String sqlWhere = " WHERE SOME_CONDITION = 'SOME_VALUE' "
for(String x : list){
sqlFrom += x +" , "+;
}
//here make sure that you remove the last comma from sqlFrom because you will get an SQLException
String finalSql = sqlSelect + sqlFrom + sqlWhere ;
I 'm using Hibernate connecting to mysql as database layer, the weird thing is for some reason
the result sets executed from Hibernate api java code are different from those executed directly from mysql. They are not a part of each other, they just look like no relationship.
Here's the Java code at Dao layer:
int totalPage = 0;
int reminder = total % pagination.getPageSize();
if(total == 0){
totalPage = 1;
pagination.setTotalPage(totalPage);
}else{
totalPage = reminder == 0 ? total / pagination.getPageSize() : (total - reminder) / pagination.getPageSize() + 1;
pagination.setTotalPage(totalPage);
}
pagination.setTotalRows(total);
pagination.setBeginAndEndPage(total);
final StringBuilder queryString = new StringBuilder();
queryString.append(" select a.target_id as itemId, a.isFree as isFree, ");
queryString.append(" ni.fullName as item, nc.fullName as category, a.downloadCount as downloadCounts from( ");
queryString.append(" select dldc.current_category_id, dldc.target_id, dldc.isFree, ");
queryString.append(" sum(dldc.counts) as downloadCount from download_log_day_count dldc ");
queryString.append(" group by dldc.current_category_id, dldc.target_id) as a ");
queryString.append(" left join item i on a.target_id = i.objectId ");
queryString.append(" left join name ni on i.nameId = ni.objectId ");
queryString.append(" left join category c on a.current_category_id = c.objectId ");
queryString.append(" left join name nc on c.nameId = nc.objectId ");
queryString.append(" order by downloadCounts desc");
List<ItemReportVO> reportList = (List<ItemReportVO>)getJpaTemplate().execute(new JpaCallback(){
#Override
public Object doInJpa(EntityManager em)
throws PersistenceException {
SQLQuery query = ((Session)em.getDelegate()).createSQLQuery(queryString.toString());
query.addScalar("itemId", Hibernate.LONG);
query.addScalar("isFree", Hibernate.BOOLEAN);
query.addScalar("item", Hibernate.STRING);
query.addScalar("category", Hibernate.STRING);
query.addScalar("downloadCounts", Hibernate.LONG);
query.setResultTransformer(Transformers.aliasToBean(ItemReportVO.class));
int firstResult = (pagination.getCurrentPage()-1) * pagination.getPageSize();
query.setFirstResult(firstResult);
if(token != null && "all".equals(token)){
int maxResult = pagination.getPageSize();
query.setMaxResults( maxResult );
}else{
int maxResult = pagination.getPageSize() > Integer.valueOf(token) ? Integer.valueOf(token) : pagination.getPageSize();
query.setMaxResults( maxResult );
}
return query.list();
}
});
When I set show_sql=true in Hibernate's config file, Hibernate prints the sql query below when Java code runs "return query.list();":
select a.target_id as itemId, a.isFree as isFree, ni.fullName as item, nc.fullName as category, a.downloadCount as downloadCounts
from(
select dldc.current_category_id, dldc.target_id, dldc.isFree, sum(dldc.counts) as downloadCount
from download_log_day_count dldc
group by dldc.current_category_id, dldc.target_id) as a
left join item i on a.target_id = i.objectId
left join name ni on i.nameId = ni.objectId
left join category c on a.current_category_id = c.objectId
left join name nc on c.nameId = nc.objectId
order by downloadCounts desc
limit ?
I always consider they should be the same, but it seems they are not, anybody can help me to adjust the java codes?
Hibernate will alter your query to apply limit that you coded via API setMaxResults(). It should also add rownum> for setFirstResult() but, in your case, it may be very first query.
You don't have to change anything as Hibernate applies those in database specific and correct way.
Is there any other difference that concerns you?