How to keep Quotes For Making proper SQL Query - java

When i run the below program
package com.util;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> list_of_symbols = new ArrayList<String>();
list_of_symbols.add("ABB");
list_of_symbols.add("ACC");
list_of_symbols.add("SBIN");
StringBuilder sb_builder = new StringBuilder();
for (int i = 0; i < list_of_symbols.size(); i++) {
sb_builder.append(list_of_symbols.get(i) + ",");
}
String sql = "Select * from data where symbol_name IN ("
+ sb_builder.deleteCharAt(sb_builder.length() - 1).toString()
+ ")";
System.out.println(sql);
}
}
The Result of SQL IS
Select * from data where symbol_name IN (ABB,ACC,SBIN)
Where as the expected result should be
Select * from data where symbol_name IN ('ABB','ACC','SBIN')
Could you please let me know how can i keep Quotes so that it becomes valid SQL

Don't use string concatenation to fill in SQL parameters. It's error-prone. Instead, build the SQL with as many ? as you need, and then use a PreparedStatement and as many setString(x, theString) as you need to fill in the ?.
In your case, it would look roughly like this:
package com.util;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> list_of_symbols = new ArrayList<String>();
list_of_symbols.add("ABB");
list_of_symbols.add("ACC");
list_of_symbols.add("SBIN");
// Build the statement
StringBuilder sql = new StringBuilder(200);
sql.append("Select * from data where symbol_name IN (");
for (int i = 0; i < list_of_symbols.size(); i++) {
sql.append(i == 0 ? "?" : ", ?");
}
sql.append(')');
// Build the PreparedStatement and fill in the parameters
PreparedStatement ps = someConnection.prepareStatement(sql.toString());
for (int i = 0; i < list_of_symbols.size(); i++) {
ps.setString(i + 1, list_of_symbols.get(i));
}
// Do it
ResultSet rs = ps.executeQuery();
}
}
(This is un-optimized, and dashed off. Some editing is likely required.)
This site has a good explanation of why using string concat for parameters is a bad idea, as well as practical examples of how to do things properly in many languages, including Java.

As you know the number of values in your list I'd suggest to put in one ? per IN value and make it a PreparedStatement.
Then simply do
ps.setString (n, nthString);
In a loop for 1..n parameters and your driver will handle proper escaping.
Like this:
List<String> list_of_symbols = new ArrayList<String>();
list_of_symbols.add("ABB");
list_of_symbols.add("ACC");
list_of_symbols.add("SBIN");
StringJoiner join = new StringJoiner(",",
"Select * from data where symbol_name IN (", ")");
for (int i = 0; i < list_of_symbols.size(); i++) {
join.add("?");
}
PreparedStatement ps = aConnection.prepareStatement(join.toString());
for (int i = 0; i < list_of_symbols.size(); i++) {
ps.setString(i+1, list_of_symbols.get(i));
}

When making your list, use this:
sb_builder.append("'"+list_of_symbols.get(i) + "',");
Notice the ' on the string. This works for simple cases only, where strings do not have ' and you're not worried with SQL Injection. For more complex cases, used PreparedStatements and add a list of ?. Then replace the ? with the Strings you want to use.

Related

How to concatenate sql query in java?

I'm beginning to learn sql and java and I have a problem.
The logic for the code is:
The first part of the sql statement should be "a.stdn_code_ts" and as more elements (in this case student code) are included, I need to concatenate with and OR-Statement.
student codes could be a single value or a range, say for example: '567777' is valid as well as '567777-876677'.
If it is single value, just add "=" then the student code. In the example if the user entered '567777' then the query should be something like "a.stdnt_code_ts = '567777'"
If it is a range, add the first student code then "BETWEEN" second code. I.e.: if the user entered '567777-876677', the query should be "a.stdnt_code_ts BETWEEN '567777' AND '876677'".
and as I mentioned above, if there are 2 or more student codes the query should be concatenated with an "OR a.stdnt_code_ts" then checks again if it is a single value or a range.
I already have this code and got stuck:
private void formatStudentCode(Connection connection) throws Exception {
studentCode = "a.stdnt_code_ts ";
for(int i=0; i < stdntCode.size(); i++) {
if (stdntCode.get(i).indexOf("-")==-1) {
studentCode += "= '" + stdntCode.get(i) + "'";
}
else {
String [] range=stdntCode.get(i).split("-");
studentCode += "BETWEEN '" + range[0] + "' AND '" + range[1] +
"'";
}
}
}
First, this code is incomplete, so I'll need to make some guesses. But let's try.
Let's fix the loop first:
String sql = "SELECT * FROM students";
List<String> terms = new ArrayList<>();
List<String> arguments = new ArrayList<>();
// Don't need index here, for each loop is better
for (String code : codes) {
// No need for IndexOf
if (code.contains("-")) {
terms.add("stdnt_code_ts between ? and ?");
String[] split = code.split("-");
arguments.add(split[0]);
arguments.add(split[1]);
}
else {
// Don't concatenate SQL query parameters
terms.add("stdnt_code_ts = ?");
arguments.add(code);
}
}
Now to put our OR:
if (terms.size() > 0) {
sql += " WHERE " + Strings.join(terms, " OR ");
}
Now to add actual parameters for each question mark:
PreparedStatement preStmt = conn.prepareStatement(sql);
int count = 0;
for (String code : arguments) {
preStmt.setString(++count, code);
}
And finally to execute the query:
ResultSet rs = preStmt.executeQuery();
Note that I'm not running this code, so I may miss a line or two, but that's the general idea of how it should be done correctly.

java return String[] in method,String[].length is not correct

What I want is inserting datas into array String[],and then print the array values.
the returning String[] type method is
public String[] getRequirementDocIDofProject(String testprojectName)
throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
String req_doc_ids[] = null;
String str_sqlQuery = "select * from req_specs INNER JOIN nodes_hierarchy nh " +
"on nh.id=req_specs.testproject_id " +
"INNER JOIN requirements reqs " +
"on req_specs.id =reqs.srs_id where nh.name='" + testprojectName + "'";
int count = 0;
int n = 0;
initDB();
resultSet = statement.executeQuery(str_sqlQuery);
while (resultSet.next()){
count = Integer.parseInt(resultSet.getString(1));
}
req_doc_ids = new String[count];
resultSet = statement.executeQuery(str_sqlQuery);
while (resultSet.next()) {
req_doc_ids[n] = resultSet.getString("req_doc_id");
System.out.println("REQID=" + req_doc_ids[n]);
n++;
}
close();
System.out.println("n==" + n);
return req_doc_ids;
}
the calling method code is
DBConnection dbcon = new DBConnection();
String req_doc_ids[] = dbcon.getRequirementDocIDofProject("XXXX");
System.out.println(req_doc_ids.length);
the print message in Console is
REQID=TECH-6104
REQID=TECH-6686
REQID=TECH-5391
REQID=TECH-5965
REQID=TECH-6530
REQID=TECH-6729
REQID=TECH-7082
REQID=TECH-7107
REQID=TECH-7184
n==9
7166
why req_doc_ids.length 's value is 7166 rather than 9
7166 comes from the 1-th column of the result set - it's the value in the last row.
while(resultSet.next()){
count=Integer.parseInt(resultSet.getString(1));
}
Instead, you perhaps meant:
while(resultSet.next()){
count++;
}
Mind you, this is an unnecessarily inefficient way to create the array. Use a List instead; or, use the method on the result set API to get the number of rows directly.
Your main issue has already been clarified by Andy and this answer is just an extension to help you with your current code.
Couple of improvements possible in your code.
Prefer using PreparedStatement instead of Statement which is not safe and may be at risk to SQL Injection attack as already mentioned by Jon Skeet.
Why to run a db query twice which potentially could be a heavy query just to find out the number of records to correctly initialize your String array.
Use List<String> to store whatever number of rows you get and finally convert the list to array like shown in below code.
Get rid of so many unneeded variables and so many lines of code to make your code appear crisp and clear.
You may try changing your method to this one,
public String[] getRequirementDocIDofProject(String testprojectName)
throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
List<String> reqDocIdList = new ArrayList<String>();
String str_sqlQuery = "select * from req_specs INNER JOIN nodes_hierarchy nh " +
"on nh.id=req_specs.testproject_id " +
"INNER JOIN requirements reqs " +
"on req_specs.id =reqs.srs_id where nh.name='" + testprojectName + "'";
initDB();
resultSet = statement.executeQuery(str_sqlQuery);
while (resultSet.next()){
System.out.println("REQID=" + resultSet.getString("req_doc_id"));
reqDocIdList.add(resultSet.getString("req_doc_id"));
}
close();
System.out.println("n==" + reqDocIdList.size());
return reqDocIdList.toArray(new String[reqDocIdList.size()]);
}

How sql works and why this result is returned?

Hi I'm using preparedStatement in Java to execute query in DB.
The table:
When it comes to update, delete and insert it's all fine, however when it comes to select( ex. I've done "SELECT ?,?,?,?,? from person" and set strings afterwards) and the following result is returned:
I'm assuming that because it's the strings that are replacing ? so it did not come out as expected:(please correct me if it's wrong)
Expected sql: "SELECT no,name,tel,birthday,address FROM person"
Actual sql: "SELECT \"no\",\"name\",\"birthday\",\"address\" FROM person"
I've tested the second one in in Navicat:
I'd like to understand that why executing this query statement would return a result like this?
If it would help here's Java code:
// Data Assist Object
public class DAO {
static String jdbcurl;
static String username;
static String password;
static{
try {
Class.forName("com.mysql.jdbc.Driver");
ResourceBundle rb = ResourceBundle.getBundle("db");
jdbcurl = rb.getString("jdbcurl");
username = rb.getString("username");
password = rb.getString("password");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
// for insert, delete and update
public int modify(String sql, String[] args){
int x=0;
try(Connection con = DriverManager.getConnection( jdbcurl,username ,password);
PreparedStatement ps = con.prepareStatement(sql);){
for (int i = 0; i < args.length; i++) {
ps.setString(i+1, args[i]);
}
x =ps.executeUpdate();
System.out.println(x);
}catch(SQLException e){
e.printStackTrace();
}
return x;
}
// for select
public List<Map<String,String>> query(String sql, String[] params){
List<Map<String,String>> resList = new ArrayList<>();
try(Connection con = DriverManager.getConnection( jdbcurl,username ,password);
PreparedStatement ps = con.prepareStatement(sql);){
for (int i = 0; i < params.length; i++) {
ps.setString(i+1, params[i]);
}
try(ResultSet res =ps.executeQuery();){
ResultSetMetaData mdata = res.getMetaData();
int num = mdata.getColumnCount();
while(res.next()){
HashMap<String,String> data = new HashMap<>();
for (int i = 1; i <= num; i++) {
String result = res.getString(i);
String columnName = mdata.getColumnName(i);
data.put(columnName,result);
}
resList.add(data);
}
}
}catch(Exception e){
e.printStackTrace();
}
return resList;
}
public static void main(String[] args) throws SQLException {
DAO dao = new DAO();
String sql = "insert into person(name,tel,birthday,address) values(?,?,?,?)";
sql = "select ?,?,?,?,? from person";
List<Map<String,String>> res = dao.query(sql, new String[]{"no","name","tel","birthday","address"});
for(Map m:res){
System.out.print("no: "+m.get("no")+",");
System.out.print("name: "+m.get("name")+",");
System.out.print("tel: "+m.get("tel")+",");
System.out.print("birthday: "+m.get("birthday")+",");
System.out.println("address: "+m.get("address"));
}
}
}
Thanks for any help.
SQL basically works on a show me these columns where this criteria is true basis.
In the statement:
"SELECT \"no\",\"name\",\"birthday\",\"address\" FROM person"
You're getting
SELECT "no", "name", "birthday", "address" FROM person
when it actually hits the database. The "" operator creates a string in SQL. In plain English, that means that you're telling the database to return that specified set of strings for each row in person where the criteria you listed is met.
Since you didn't list a where clause, all rows are true by default so you get one row of strings for every single row in the person table. The first query is the same thing, but instead of directly passing the strings, you're adding them in as bind variables.
If you actually want to see the values in the table, write the query without the "'s
SELECT no, name, birthday, address FROM person
Unless otherwise specified, bind functions generally pass the value as a string. Which is why the query behaved the way it did. I don't recommend using bind variables in the select clause. That's a strange practice.
Edit:
As Adrian pointed out in the comments, " denotes columns in SQL. My apologies for not catching that. I assume that you meant to use the ' operator which actually denotes strings.
If not, something else is going on here entirely.
For the select you use the question marks in the WHERE clause, not where you list the fields you need as output.
Replace
sql = "select ?,?,?,?,? from person";
with
sql = "select no,name,tel,birthday,address from person";
For this particular query there is no binding to do. It will retrieve all the records from the table.

Trim Java String and use in Sql Where Clause

I am trying to get values from MySQL db using a query which gets values from a multiple select. The problem is that I am using StringBuilder to create the query based on the values I am getting. If you see the Where clause used why me, every time a new variable is fetched a comma is added in the end.
How do I remove the last comma added as it is giving me an error in the sql query. Will appreciate any help.
String[] stat = req.getParameterValues("status");
StringBuilder sb = new StringBuilder(200);
if (stat != null) {
for (int i = 0; i < stat.length; i++) {
sb.append("'" + stat[i] + "',");
}
String app = "WHERE status in (" + sb.toString() + ")";
String sqlquery = "SELECT * FROM Table " + app + ";";
}
Result Query:
SELECT * FROM Table
WHERE status in ('Deployed','PendingDisposal','Available','Reserved','Broken',);
I want to get rid of the last comma after 'Broken'
Your problem is with comma in the end, you can append your Strings like this :
String del = "";
for (int i = 0; i < stat.length; i++) {
sb.append(del).append("'" + stat[i] + "'");
del = ",";
}
But in fact this is not really good idea to use this way, because it can cause SQL Injection or syntax error, you have to use PreparedStatement for example :
for (int i = 0; i < stat.length; i++) {
sb.append(del).append("?");
del = ",";
}
try (PreparedStatement ps = connection.prepareStatement(sb) {
for (int i = 0; i < stat.length; i++) {
ps.setString(i, stat[i]);
}
ResultSet result = preparedStatement.executeQuery();//get results
...
}
You can join a string array with commas using Java 8 streams:
String joined = Arrays.asList(stat).stream()
.map(s -> "'" + s + "'")
.collect(Collectors.joining(", "));
But note that joining strings like this is a really bad idea: you are vulnerable to SQL injection attacks, e.g. if one of the element of stat is something like:
'); DROP TABLES Table; --
You are better off building the query using a PreparedStatement. See this question.
Need to update your for loop where you need to append element from array till its second last element and append last element of the array outside the loop.
for(int i=0;i<stat.length-1;i++){ // loop till second last element
sb.append("'"+stat[i]+"',"); //appends , at end
}
sb.append("'"+stat[stat.length-1]+"'"); // append last element
You can use deleteChartAt method:
sb.deleteCharAt(sb.length()-1);
For example:
String[] stat = req.getParameterValues("status");
StringBuilder sb = new StringBuilder(200);
if(stat!=null) {
for(int i=0;i<stat.length;i++){
sb.append("'"+stat[i]+"',");
}
sb.deleteCharAt(sb.length()-1); //---------> This will remove the last comma
String app = "WHERE status in ("+sb.toString()+")";
String sqlquery = "SELECT * FROM Table "+app+";";
}
try something this.
for(int i=0;i<stat.length;i++){
sb.append("'"+stat[i]+"'");
if(i!=stat.length-1){
sb.append(",");
}
}
try
String app = "WHERE status in ("+sb.substring(0, sb.length()-2)+")";

Efficient way to select data with a single condition

Is there an efficient way to obtain a list (preferably an array, a ResultSet will do) to SELECT a lot of rows.
For example:
Connection con = DriverManager.getConnection(host,username,password);
String sql = "SELECT * FROM table_name WHERE food = ? AND expiration > ?";
PreparedStatement stmt = con.prepareStatement(sql);
Using the above code, I want to get all the food from a given array that isn't expired.
String[] foodList = {"banana","apple","orange",...}
where the expiration date is a constant date (lets say 3 days ago). However, the way I have it is that the String and PreparedStatement are in a for loop that loop the number of foods in the array to individually check the expiration date. This creates a lot of ResultSets after I execute each individually.
Most SQL Databases support a IN (list) expression. This is roughly equivalent to providing a or expression:
SELECT id FROM table WHERE food IN ('Apple', 'Banana') AND exp < ?
is similar to
SELECT id FROM table WHERE (food = 'Apple' or food = 'Banana') AND exp < ?
In both cases some RDBMS can optimize it.
However first of all there is a limitation in the number of list items you can specify in the IN or number of characters you can use in the statement. So if your list can be variable long you need to be prepared to run multiple statements.
Secondly you cannot* set a array as an argument to a PreparedStatement and expect it to work with IN.
Unfortunately in plain JDBC all you can do is to concatenate a String. This is frowned upon, but there is no good alternative (unless you want to do something like giving the list of foods as a single list and use a "instring" expression).
Make sure to add as many ? (but not too many) as you expect parameters and then set them in the IN:
String[] foods = ...;
int remain = foods.length;
int start = 0;
while(remain > 0)
{ if (remain >= 100)
executeBatch(foods, start, 100); start+=100; remain-=100;
else if (remain >= 30)
executeBatch(foods, start, 30); start+=30; remain-=30;
else {
executeBatch(foods, start, 1); start+=1; remain-=1;
}
}
void executeBatch(String[] f, int off, int len)
{
StringBuilder sqlBuf = StringBuilder("... IN(");
for(int i=0;i<len;i++) {
sqlBuf.append((i!=0)?",?":"?");
}
String sql = sqlBuf.append(") AND exp < ?").toString();
PreparedStatement ps = c.prepareStatement(sql);
for(int i=0;i<foods.length;i++)
ps.setString(i+1, foods[i+off]);
ps.setTimestamp(foods.length+1, now);
....
}
This avoids to generate a lot of different SQL statement to compile. (Only 100,30 or 1 ?)). You can use the same logic for the OR case.
* not to be confused with ARRAY database type.
Probably not the most elegant solution, and you won't get any performance benefit from the prepared statement (but you will get parameter binding):
StringBuilder sql = new StringBuilder("SELECT * FROM table_name WHERE expiration > ? AND food IN (");
for (int i = 0; i < foodList.length; i++) {
if (i > 0) {
sql.append(',');
}
sql.append('?');
}
sql.append(")");
Connection con = DriverManager.getConnection(host, username, password);
PreparedStatement stmt = con.prepareStatement(sql.toString());
stmt.setDate(1, expirationDate);
for (int i = 0; i < foodList.length; i++) {
stmt.setString(i + 2, foodList[i]);
}
ResultSet rs = stmt.executeQuery();
/* ... Do Stuff ... */

Categories

Resources