I am trying to implement a functionality where I have to query a database with input parameter values. Input values are optional.
For example
I have a table student with following fields
a)student_id
b)student_roll_no
c)student_first_name
d)student_last_name .. etc
I am need to write a dao layer function so as to retrieve student details depending upon input criteria or parameters.
1) if Input contains only student_id then query should be
select * from student where student_id = :inputStudentId
2) if Input contains student_id, firstName then query should be
select * from student where student_id = :inputStudentId and first_name = :inputFirstName
like wise for other input parameters, please note input parameters can be 0 to n size
Please suggest what is the best approach to do it?
I dont wann add null checks and append the query for not null parameters. I want to try something reasonable and logical either in Java or sql (named query)
I am using java1.6 and hibernate
If you using Hibernate, you can use HSQL to do that in following manner:
query=em.createQuery("SELECT s FROM Student s WHERE s.id=:id");
query.setParameter("id",studentIt)
For exact API details, check the documentation # http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html
You can also use CriteraApi - this is made for dynamic query creation.
You can do this by dynamically building your HQL with optional Parameters something like shown below:
Map<String, Object> parameters= new HashMap<String,Object>();
parameters.put("firstName", firstName);
parameters.put("lastName", lastName);
StringBuilder hql = "SELECT student FROM Student as student where 1 = 1";
if (firstName != null) {
hql.append(" and student.firstName in :firstName");
}
if (lastName != null) {
hql.append(" and student.lastName in :lastName ");
}
Query query = session.createQuery(hql.toString());
for (String p : query.getNamedParameters()) {
query.setParameter(p, parameters.get(p));
}
Related
I am trying to generate sql query based on user input. There are 4 search fields on the UI:
FIRST_NAME, LAST_NAME, SUBJECT, MARKS
Based on user input I am planning to generate SQL query. Input can be of any combination.
eg: select * from TABLE where FIRST_NAME="some_value";
This query needs to be generated when FIRST_NAME is given and other fields are null
select * from TABLE where FIRST_NAME="some_value" and LAST_NAME="some_value";
This query needs to be generated when FIRST_NAME and LAST_NAME are given and other fields are null
Since there are 4 input fields, number of possible queries that can be generated are 24 (factorial of 4).
One idea is to write if condition for all 24 cases.
Java pseudo code:
String QUERY = "select * from TABLE where ";
if (FIRST_NAME!=null) {
QUERY = QUERY + "FIRST_NAME='use_input_value';"
}
if (LAST_NAME!=null) {
QUERY = QUERY + "LAST_NAME='use_input_value';"
}
if (SUBJECT!=null) {
QUERY = QUERY + "SUBJECT='use_input_value';"
}
if (MARKS!=null) {
QUERY = QUERY + "MARKS='use_input_value';"
}
I am not able to figure out how to generate SQL queries with AND coditions for multiple Input values.
I have been through concepts on dynamically generate sql query but couldn't process further.
Can someone help me on this.
FYI: I have been through How to dynamically generate SQL query based on user's selections?, still not able to generate query string based on user input.
Let's think about what would happen if you just ran the code you wrote and both FIRST_NAME and LAST_NAME are provided. You'll wind up with this:
select * from TABLE where FIRST_NAME='use_input_value';LAST_NAME='use_input_value';
There are two problems here:
The query is syntactically incorrect.
It contains the literals 'use_input_value' instead of the values you want.
To fix the first problem, let's first add and to the start of each expression, and remove the semicolons, something like this:
String QUERY = "select * from TABLE where";
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME='use_input_value'";
}
Notice the space before the and. We can also remove the space after where.
Now the query with both FIRST_NAME and LAST_NAME will look like this:
select * from TABLE where and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value'
Better but now there's an extra and. We can fix that by adding a dummy always-true condition at the start of the query:
String QUERY = "select * from TABLE where 1=1";
Then we append a semicolon after all the conditions have been evaluated, and we have a valid query:
select * from TABLE where 1=1 and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value';
(It may not be necessary to append the semicolon. Most databases don't require semicolons at the end of a single query like this.)
On to the string literals. You should add a placeholder instead, and simultaneously add the value you want to use to a List.
String QUERY = "select * from TABLE where";
List<String> args = new ArrayList<>();
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME=?";
args.add(FIRST_NAME);
}
After you've handled all the conditions you'll have a string with N '?' placeholders and a List with N values. At that point just prepare a query from the SQL string and add the placeholders.
PreparedStatement statement = conn.prepareStatement(QUERY);
for (int i = 0; i < args.size(); i++) {
statement.setString(i + 1, args[i]);
}
For some reason columns and parameters are indexed starting at 1 in the JDBC API, so we have to add 1 to i to produce the parameter index.
Then execute the PreparedStatement.
I have the following DAO method:
public String getSomeTable(final String param1) {
String sqlString = "select * from table where name ilike ?";
Query query = this.getEntityManager().createNativeQuery(sqlString);
query.setParameter(1, "%param1%");
}
If param1 is null or empty then I want to select all entries from the table. What is the correct way to do this? I am currently using the following:
public String getSomeTable(final String param1) {
String sqlString = "select * from table where name = ?";
Query query = this.getEntityManager().createNativeQuery(sqlString);
if(param1 == null)
query.setParameter(1, "%%");
else
query.setParameter(1, "%param1%");
}
But this is not scalable. I have datatypes like integer, date, etc. I want to know if there is a way to skip checking for that parameter if it is null.
I was planning to use COALESCE(?, CASE WHEN ? = '' THEN '%%' ELSE '%?%') but I think ? can be used only once for a particular parameter. The next one > I write is linked to second param.
On SQL Server, I use something like this, perhaps you can translate it to postgres:
DECLARE #variable INT = NULL;
SELECT *
FROM sysobjects
WHERE
(1 = CASE WHEN #variable IS NULL THEN 1 ELSE 2 END)
OR
(id LIKE #variable);
I am new to Hibernate. I am building a Login Portal. We have used DB Function to encrypt User Password. It seems that using hibernate for complex queries/functions/procedures on existing databases is difficult.
Is it possible to write below queries using Hibernate criteria?
SQL Query 1 :
SELECT first_name
FROM user.emp_group
WHERE username = 'XXX'
AND user.decrypt(password, 2) = 'YYYY';
SQL Query 2 :
SELECT a.DESC
,b.total
FROM user.STATUS a
,(
SELECT STATUS
,count(*) total
FROM user.emp
GROUP BY STATUS
) b
WHERE a.TYPE = b.STATUS (+)
User is the schema name and decrypt is function name.
I also faced problem for getting data from views which was resolved by this Stackoverflow post. How hibernate retrieve data from existing database view?
Thanks for that.
You can use native SQL with hibernate.
The way is something like this (for example):
String sql = "SELECT first_name, salary FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
List data = query.list();
for(Object object : data)
{
Map row = (Map)object;
System.out.print("First Name: " + row.get("first_name"));
System.out.println(", Salary: " + row.get("salary"));
}
I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;
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.