Building a dynamic SQL query based on user input in Java - java

If I have a search module which has the following:
search box, dropdown 1, dropdown 2.
And I have a query like this:
SELECT * FROM MY_TABLE where q1 = 'searchBox' AND q2 = 'dropdown1' AND q3 = 'dropdown2'
How can I make that query dynamic depending on user filter, so if the user only fills the search box, the query will be:
SELECT * FROM MY_TABLE where q1 = 'searchBox'
If the user fills search box and dropdown1, the query will be:
SELECT * FROM MY_TABLE where q1 = 'searchBox' AND q2 = 'dropdown1'
and if the user doesn't fill anything, the query will be:
SELECT * FROM MY_TABLE
I am using Java.

There are frameworks that can help with this:
QueryDSL
jOOQ
Squiggle
Hibernate
If you'd like to create a quick and simple solution, you can do something like the following:
List<String> params = new ArrayList<>();
StringBuilder sb = new StringBuilder();
sb.append("SELECT * FROM MY_TABLE WHERE 1 = 1");
if (searchBox != null) {
sb.append(" AND q1 = ?");
params.add(searchBox);
}
if (dropdown1 != null) {
sb.append(" AND q2 = ?");
params.add(dropdown1);
}
if (dropdown2 != null) {
sb.append(" AND q3 = ?");
params.add(dropdown2);
}
PreparedStatement preparedStatement = connection.prepareStatement(sb.toString());
for (int i = 1; i <= params.size(); i++) {
preparedStatement.setString(i, params.get(i));
}
ResultSet resultSet = preparedStatement.executeQuery();

To improve upon the code provided by #blacktide
PreparedStatement preparedStatement = connection.prepareStatement(sb.toString());
for (int i = 1; i <= params.size(); i++) {
preparedStatement.setString(i, params.get(i));
}
You would instead want the line within the for loop to be
preparedStatement.setString(i, params.get(i-1));
Since arrays start at 0, we'd want to grab that zeroth index to set the first value added to the list to the first value to be updated in the SQL string. Otherwise you would grab the second value within params which would not be the intended value.

Related

Varying content of an SQL update statement in java

I need to write an update function where its content is different based on what parameters are passed, e.g. if I have updateBook(int id, String title, String author, int pages), I have to do something like:
String sql;
if((!title.equals("null"))&&(!author.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', author='"+author+"', pages="+pages;
else if(((!title.equals("null"))&&(!author.equals("null")))
sql = "UPDATE book SET title='"+title+"', author='"+author+"'";
else if(((!title.equals("null"))&&(pages>0)))
sql = "UPDATE book SET title='"+title+"', pages="+pages;
... //and so on
sql = sql + " WHERE bookid="+id+";";
The more fields I have in my table, the more checks I have to do, which is uncomfortable, and requires me to write a lot of code.
Also, doing something like:
sql = "UPDATE book SET ";
if(!title.equals("null"))
sql = sql +"title='"+title+"',";
if(!author.equals("null"))
sql = sql+"author='"+author+"',";
if(pages>0)
sql = sql+"pages="+pages";
sql = sql + ";";
can't work since the unwanted commas cause statement errors.
You can see as well that if I have something like 6, 7, 8 etc field the checks start to get too many, and I can't also do more separated update statements as if something goes wrong I would need to rollback any query that has been done in that function.
Is there any way round to get a custom update statement having to write few code?
Firstly, use a PreparedStatement.
I would do it something like the following.
List<Object> params = new ArrayList<>();
StringBuilder sql = new StringBuilder();
if(!title.equals("null")) {
sql.append("title = ?");
params.add(title);
}
if(!author.equals("null")) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("author = ?");
params.add(author);
}
if(pages>0) {
if (sql.length() > 0) {
sql.append(", ");
}
sql.append("pages = ?");
params.add(pages);
}
if (sql.length() > 0) {
sql.insert(0, "UPDATE book SET ");
sql.append(" WHERE bookid=?");
java.sql.Connection conn = // however you obtain it
java.sql.PreparedStatement ps = conn.prepareStatement(sql.toString());
for (int i = 0; i < params.size(); i++) {
ps.setObject(i + 1, params.get(i));
}
ps.executeUpdate();
}

Mysql select and update column in one query

Is possible to merge my following queries into one? I'm using mysql as my database.
String qry = "SELECT id FROM customers WHERE completed=false AND server=?";
ps = connection.prepareStatement(qry);
ps.setString(1,getServerId());
rs = ps.executeQuery();
final Set<Long> ids = new HashSet<>();
while (rs.next()) {
ids.add(rs.getLong(1));
}
qry = "";
for (long l : ids) {
qry += "UPDATE customers SET completed=true WHERE id = "+l+"; ";
}
... execute query, close streams, and do some application logic with ids from database...
You can just
UPDATE customers SET completed=true WHERE completed=false AND server=?
Edit: You provided the information saying you need to actually use the Ids.
Select normally then build a string in the format MySql understands as a "list", it will certainly be faster than multiple update queries and will cost less bytes to send over the network. Here is the snippet:
String qry = "SELECT id FROM customers WHERE completed=false AND server=?";
ps = connection.prepareStatement(qry);
ps.setString(1,getServerId());
rs = ps.executeQuery();
final Set<Long> ids = new HashSet<>();
while (rs.next()) {
ids.add(rs.getLong(1));
}
qry = "";
if(ids.size()>0) {
StringBuilder idsAsString = new StringBuilder();
for(int i = 0; i < ids.size(); i++) {
idsAsString.append(ids.get(i));
if(i < ids.size()-1)
idsAsString.append(",");
}
qry = "UPDATE customers SET completed=true WHERE id IN ("+idsAsString+"); ";
}
instead of building multiple UPDATE statements in a for loop.

Getting row count with Result Set

Please Help !!!!!!!
In DB I have 2 rows with this query.
SELECT D.DEALER_CODE
FROM SCOTT.T_DEALERSHIP D,SCOTT.T_DEALER_BILLING DB
WHERE D.DEALER_CODE = DB.DEALER_CODE
AND DEALER_NAME LIKE 'XTIME%'
AND (RNR_CUST_NUM = '546' OR RNR_CUST_NUM = '43356'OR RNR_CUST_NUM = '7637055' OR RNR_CUST_NUM ='7637055' OR RNR_CUST_NUM IS NULL)
AND (RCI_STORE_NUMBER IS NULL OR RCI_STORE_NUMBER = '05')
AND (RCI_AREA_NUMBER = '01'OR RCI_AREA_NUMBER IS NULL)
AND (RCI_DEALER_NUMBER IS NULL AND DEALER_Address1 LIKE UPPER('1500 ORACLE%')
AND DEALER_CITY =UPPER('BAKERSFIELD') AND DEALER_ZIP LIKE'6%')
With below code, I get only 1 row when there are 2 records in DB. When there is only one record in DB it works but not when there are more records. Also rs.last() method gets skipped and exits resultset. I have to comment it to execute for one record. I do not want to use COUNT.
ResultSet rs = stmt.executeQuery(sql1);
System.out.println("" + sql1);
while(rs.next()) {
rs.last();
int Rows = rs.getRow();
System.out.println("Rows are " + Rows);
}
If all you want to do is count the records in the database, then you should really use a query of the form SELECT count(*) FROM .... If you really need to perform the query the way you listed above, then you want to set a counter variable outside your while loop like so:
int rowNum = 0;
while (rs.next()) {
rowNum++;
}
System.out.println("Number of records: "+rowNum);
I strongly recommend against returning all the data to Java via JDBC just to determine the number of rows. If you just want to know how many rows match the condition, then do a select count(*) query:
In SQL:
SELECT COUNT(*)
FROM SCOTT.T_DEALERSHIP D,SCOTT.T_DEALER_BILLING DB
WHERE D.DEALER_CODE = DB.DEALER_CODE
AND DEALER_NAME LIKE 'XTIME%'
AND (RNR_CUST_NUM = '546' OR RNR_CUST_NUM = '43356'OR RNR_CUST_NUM = '7637055' OR RNR_CUST_NUM ='7637055' OR RNR_CUST_NUM IS NULL)
AND (RCI_STORE_NUMBER IS NULL OR RCI_STORE_NUMBER = '05')
AND (RCI_AREA_NUMBER = '01'OR RCI_AREA_NUMBER IS NULL)
AND (RCI_DEALER_NUMBER IS NULL AND DEALER_Address1 LIKE UPPER('1500 ORACLE%')
AND DEALER_CITY =UPPER('BAKERSFIELD') AND DEALER_ZIP LIKE'6%')
Then in Java:
ResultSet rs = stmt.executeQuery(sql1);
System.out.println("" + sql1);
int rows = 0;
if(rs.next()) {
rows = rs.getInt();
}
System.out.println("Rows are " + rows);
Edit based on OP's comments
To get the values from the database in addition to the count, just scan the values.
SQL:
SELECT D.DEALER_CODE
FROM SCOTT.T_DEALERSHIP D,SCOTT.T_DEALER_BILLING DB
WHERE D.DEALER_CODE = DB.DEALER_CODE
AND DEALER_NAME LIKE 'XTIME%'
AND (RNR_CUST_NUM = '546' OR RNR_CUST_NUM = '43356'OR RNR_CUST_NUM = '7637055' OR RNR_CUST_NUM ='7637055' OR RNR_CUST_NUM IS NULL)
AND (RCI_STORE_NUMBER IS NULL OR RCI_STORE_NUMBER = '05')
AND (RCI_AREA_NUMBER = '01'OR RCI_AREA_NUMBER IS NULL)
AND (RCI_DEALER_NUMBER IS NULL AND DEALER_Address1 LIKE UPPER('1500 ORACLE%')
AND DEALER_CITY =UPPER('BAKERSFIELD') AND DEALER_ZIP LIKE'6%')
Java:
ResultSet rs = stmt.executeQuery(sql1);
System.out.println("" + sql1);
List<String> rows = new ArrayList<String>();
while(rs.next()) {
rows.add(rs.getString());
}
System.out.println("There are " + rows.size() + " rows.");

Building a String parameter (with SQL content) from resource file for SQL PreparedStatement

I need to execute a SQL PreparedStatement in Java using jdbc.
I'm facing problems with one of the parameters because it has SQL content and also Strings from a resource file.
It looks something like this:
Required SQL:
SELECT * FROM Table T WHERE T.value = 10 AND T.display IN ('Sample1', 'Sample2')
In the above query, the Sample1 and Sample2 values must be passed through a parameter to a PreparedStatement.
PreparedStatement:
SELECT * FROM Table T WHERE T.value = 10 ?
In my application code I'm setting the parameters like:
statement.setString(1, "AND T.display IN ('Sample1', 'Sample2')");
However this is not returning the appropriate results.
Is there a better way to build this particular parameter considering it has SQL content and Strings too?
EDIT:
Sample1, Sample2 etc. are strings that are retrieved from an external file at run-time and there can be different number of these strings each time. I.e. there can be only one string Sample1 or multiple strings Sample1, Sample2, Sample3, etc..
EDIT2:
Database being used is Oracle.
The ? placeholder can only be used in a position where a value is expected in the query. Having a ? in any other position (as in your question: WHERE T.value = 10 ?) is simply a syntax error.
In other words: it is not possible to parametrize part of the query itself as you are trying to do; you can only parametrize values. If you need to add a dynamic number of parameters, you will need to construct the query dynamically by adding the required number of parameters and using setString(). For example:
StringBuilder sb = new StringBuilder(
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?");
// Note: intentionally starting at 1, first parameter already above
// Assuming always at least 1 parameter
while (int i = 1; i < params.length; i++) {
sb.append(", ?");
}
sb.append(')');
try (
PreparedStatement pstmt = con.prepareStatement(sb.toString())
) {
for (int i = 0; i < params.length; i++) {
pstmt.setString(i + 1, params[i]);
}
try (
ResultSet rs = pstmt.executeQuery();
) {
// Use resultset
}
}
Use this as PreparedStatement
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?, ?);"
and then call
statement.setString(1, "Sample1");
statement.setString(2, "Sample2");
before executing the statement.
Update:
String generateParamString(int params) {
StringBuilder sb = new StringBuilder("(");
for (int i = 1; i < params; i++) {
sb.append("?, ");
}
sb.append("?)");
return sb.toString();
}
List<String> samples = ... // your list with samples.
String stmtString = "SELECT * FROM Table T WHERE T.value = 10 AND T.display IN "
+ generateParamString(samples.size());
// generate statement with stmtString
for (int i = 0; i < samples.size(); i++) {
statement.setString(i + 1, samples.get(i));
}
// execute statement...

ResultSet returns only last row into JTable

I have done enough searches to solve my problem which i have done partly but there's this one bug that keeps disturbing me.I am trying to fetch data from a database based on a condition.I have a table 'user_branch' with a foreign key column branchID which is supposed to fetch the coresponding branchNames in another table 'branches' and I am supposed to display the results into a JTable.When i do System.out.println i get all my results but it returns only the last row when i display in a JTable(branchJTable).This is the code i am using
int row = user2BAssignedJTable.getSelectedRow();
assignUserID.setText(user2BAssignedJTable.getModel().getValueAt(row, 0).toString());
user2BAssignedField.setText(user2BAssignedJTable.getModel().getValueAt(row, 1).toString());
try {
String userBrQry = "SELECT branchID FROM user_branch WHERE userID IN(?) ";
String brQ = "SELECT branchName FROM branches WHERE branchID IN(%s) ";
pstmt = con.prepareStatement(userBrQry);
pstmt.setString(1, assignUserID.getText());
results = pstmt.executeQuery();
results.last();
int nRows = results.getRow();
results.beforeFirst();
while (results.next()) {
String branchIDS = results.getString("branchID");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < nRows; i++) {
builder.append("?");
if (i + 1 < nRows) {
builder.append(",");
}
}
brQ = String.format(brQ, builder.toString());
PreparedStatement ps = con.prepareStatement(brQ);
for (int i = 0; i < nRows; i++) {
ps.setString(i + 1, branchIDS);
}
ResultSet rs = ps.executeQuery();
//branchJTable.setModel(DbUtils.resultSetToTableModel(rs));
javax.swing.table.DefaultTableModel model = new javax.swing.table.DefaultTableModel();
model.setColumnIdentifiers(new String[]{"Branch Name"});
branchJTable.setModel(model);
while (rs.next()) {
String branchname = rs.getString("branchName");
model.addRow(new Object[]{branchname});
}
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
}
Forget about the first 3 rows as it is a another JTable event i use to get the userID to use as a condition for getting a particular user's branches assigned to him.
The branches assigned to a user is dynamic hence using StringBuilder.
I am supposed to display the results into another JTable called branchJTable which only displays the last row.Any HELP would be appreciated!
From your question, I think you should declare the JTable
javax.swing.table.DefaultTableModel model = new javax.swing.table.DefaultTableModel();
model.setColumnIdentifiers(new String[]{"Branch Name"});
branchJTable.setModel(model);
before your first loop -
i.e. before while (results.next()) { in your code.
Otherwise in loop, for each loop execution,
the JTable Model is initialising and you are getting the last inserted row in Jtable.

Categories