Prepared Statement setString adding unnecessary single quotes to String - java

Background
I am trying to set the contents of an ArrayList into an IN clause in a Db2 SQL statement. I am using the PreparedStatement to build my query. This is our coding standard.
What I tried #1
I researched a couple ways to achieve this. I first tried using the setArray() as show in this question: How to use an arraylist as a prepared statement parameter The result was I was getting a error of Err com.ibm.db2.jcc.am.SqlFeatureNotSupportedException: [jcc][t4][10344][11773][3.65.110] Data type ARRAY is not supported on the target server. ERRORCODE=-4450, SQLSTATE=0A502 After this roadblock, I moved on to #2
What I tried #2
I then tried using the Apache Commons StringUtils to convert the ArrayList into a comma separated String like I needed for my IN clause. The result is that this did exactly what I needed, I have a single String with all my results separated by a comma.
The problem:
The setString() method is adding single quotes to the beginning and end of my String. I have used this many times, and it has never done this. Does anyone know if there is a way around this, or an alternative using the PreparedStatement?? If I use String concatenation my query works.
Code (explained above):
List<String> selectedStatuses = new ArrayList<String>(); //Used to store contents of scoped var
//Get Contents of Checkbox which are in the form of a List
selectedStatuses = (List) viewScope.get("selectedStatuses");
String selectedStatusesString = StringUtils.join(selectedStatuses, ",");
.... WHERE ATM_DET_ATM_STAT IN (?)";
ps.setString(1, selectedStatusesString);
Log Value showing correct value of String
DEBUG: selectedStatusesString: 'OPEN','CLOSED','WOUNDED','IN PROGRESS'
Visual of incorrect result
The quotes at the beginning and end are the problem.

For an IN clause to work, you need as many markers as you have values:
String sql = "SELECT * FROM MyTable WHERE Stat IN (?,?,?,?)";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "OPEN");
stmt.setString(2, "CLOSED");
stmt.setString(3, "WOUNDED");
stmt.setString(4, "IN PROGRESS");
try (ResultSet rs = stmt.executeQuery()) {
// use rs here
}
}
Since you have a dynamic list of values, you need to do this:
List<String> stats = Arrays.asList("OPEN", "CLOSED", "WOUNDED", "IN PROGRESS");
String markers = StringUtils.repeat(",?", stats.size()).substring(1);
String sql = "SELECT * FROM MyTable WHERE Stat IN (" + markers + ")";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int i = 0; i < stats.size(); i++)
stmt.setString(i + 1, stats.get(i));
try (ResultSet rs = stmt.executeQuery()) {
// use rs here
}
}
Starting with Java 11, StringUtils is no longer needed:
String markers = ",?".repeat(stats.size()).substring(1);

Use two apostrophes '' to get a single apostrophe on DB2, according to the DB2 Survival Guide. Then call .setString().

To anyone else experiencing the issue with single quotes, I had to modify my function so that it doesn't use ? to set the value; instead, I just treat the entire query as a string:
public static void runQuery(String tableName, String columnName, int value, String whereName, String whereValue) {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("UPDATE " + tableName + " SET " + columnName + " = " + value + " WHERE " + whereName + " = " + "'" + whereValue + "'")) {
ps.executeUpdate();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
Hope this helps

Related

setString for prepared statement not working

I am trying to use the setString(index, parameter) method for Prepared Statements in order to create a ResultSet but it doesn't seem to be inserting properly. I know the query is correct because I use the same one (minus the need for the setString) in a later else. Here is the code I currently have:
**From what I understand, the ps.setString(1, "'%" + committeeCode + "%'"); is supposed to replace the ? in the query but my output says otherwise. Any help is appreciated.
public String getUpcomingEvents(String committeeCode) throws SQLException{
Context ctx = null;
DataSource ds = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
StringBuilder htmlBuilder = new StringBuilder();
String html = "";
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup("java:ConnectDaily");
conn = ds.getConnection();
if(committeeCode != null){
//get all events
String queryStatement = "SELECT " +
.......
"WHERE c.calendar_id = ci.calendar_id AND c.short_name LIKE ? " +
"AND ci.style_id = 0 " +
"AND ci.starting_date > to_char(sysdate-1, 'J') " +
"AND ci.item_type_id = cit.item_type_id " +
"ORDER BY to_date(to_char(ci.starting_date), 'J')";
ps = conn.prepareStatement(queryStatement);
ps.setString(1, "'%" + committeeCode + "%'");
System.out.println(queryStatement);
rs = ps.executeQuery();
if (rs != null){
while(rs.next()){
String com = rs.getString("name");
String comID = rs.getString("short_name");
String startTime = rs.getString("starting_time");
String endTime = rs.getString("ending_time");
String name = rs.getString("contact_name");
String desc = rs.getString("description");
String info = rs.getString("contact_info");
String date = rs.getString("directory");
htmlBuilder.append("<li><a href='?com="+committeeCode+"&directory=2014-09-10'>"+com+" - "+ date +" - "+startTime+" - "+endTime+"</a> <!-- Link/title/date/start-end time --><br>");
htmlBuilder.append("<strong>Location: </strong>"+comID+"<br>");
htmlBuilder.append("<strong>Dial-In:</strong>"+com+"<br>");
htmlBuilder.append("<strong>Part. Code:</strong>"+info+"<br>");
htmlBuilder.append("<a href='http://nyiso.webex.com'>Take me to WebEx</a>");
htmlBuilder.append("</li>");
}
}
html = htmlBuilder.toString();
.
.
.
}catch (NamingException e) {
e.printStackTrace();
//log error and send error email
} catch (SQLException e) {
e.printStackTrace();
//log error and send error email
}finally{
//close all resources here
ps.close();
rs.close();
conn.close();
}
return html;
}
}
Output
14:18:22,979 INFO [STDOUT] SELECT to_char(to_date(to_char(ci.starting_date), 'J'),'mm/dd/yyyy') as start_date, to_char(to_date(to_char(ci.ending_date), 'J'),'mm/dd/yyyy') as end_date, to_char(to_date(to_char(ci.starting_date), 'J'),'yyyy-mm-dd') as directory, ci.starting_time, ci.ending_time, ci.description, cit.description as location, c.name, c.short_name, ci.add_info_url, ci.contact_name, ci.contact_info FROM calitem ci, calendar c, calitemtypes cit WHERE c.calendar_id = ci.calendar_id AND c.short_name LIKE ? AND ci.style_id = 0 AND ci.starting_date > to_char(sysdate-1, 'J') AND ci.item_type_id = cit.item_type_id ORDER BY to_date(to_char(ci.starting_date), 'J')
There is no need for the quotes in setString:
ps.setString(1, "%" + committeeCode + "%");
This method will bind the specified String to the first parameter. It will not change the original query String saved in queryStatement.
The placeholder remains as part of the SQL text.
The bind value is passed when the statement is executed; the actual SQL text is not modified. (This is one of the big advantages of prepared statements: the same exact SQL text is reused, and we avoid the overhead of a hard parse.
Also note that you are including single quotes within the value, which is a bit odd.
If the bind placeholder were to be replaced in the SQL text, assuming committeeCode contains foo, the equivalent SQL text would be:
AND c.short_name LIKE '''%foo%'''
which will match only c.short_name values that begin and end with a single quote, and contain the string foo.
(This looks more like Oracle SQL syntax than it does MySQL.)
As we know that in setString we can pass string value only, So even if we write the code like this:
String param="'%"+committeeCode+"%'";
And if you print the value of param it will throw error, Hence you cannot use it as well in prepared statement.
You need to modify modify it little bit as:
String param="%"+committeeCode+"%";(Simpler one, other way can be used)
ps.setString(1,param);

SQL exception, Generated keys not requested

This exception is occur in mentioned section of my code:
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
String query = "Insert into ...";
try {
con = DriverManager.getConnection(...);
ps = con.prepareStatement(query, java.sql.Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate(query);
rs = ps.getGeneratedKeys(); // Exception is here
}
while (resultset.next()) {
id = String.valueOf(resultset.getInt(1));
}
Exception:
Generated keys not requested. You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate() or Connection.prepareStatement()
My purpose is inserting a new record and save the first field (id) (that is auto_increment) to variable id.
You are using the wrong execute method. Instead of the one taking a String, you should use one without a parameter. And as Chris Joslin mentioned, for INSERT it is better to use executeUpdate.
Technically a correct JDBC driver should throw an SQLException immediately when calling execute(String) or one of its siblings on a PreparedStatement, but some drivers ignore this rule.
Try ps.executeUpdate() instead of ps.execute().
Shouldn't it be:
String query = "Insert into Books(Name,ISBN,Status,Date)" +
"values( '" + name + "','" + isbn + "','" + status+ "','" + date + "' ) ";
try {
con = DriverManager.getConnection(...);
ps = con.prepareStatement(query,java.sql.Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
rs = ps.getGeneratedKeys(); // Exception is here
}
It looks like in your first example, you do a prepare correctly, but then call the executeUpdate with the Query String again instead of just the ps.executeUpdate().
ps = con.prepareStatement(query, java.sql.Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate(query);

Get the data from mysql rows and display them separately by the ResultSet in Java

I have a mysql table user which is consisted of id, name, password and email columns.
Is there a way to create some sort of query or java code that will print in my message dialog window all of the users names.
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/mydb","root","");
String sql = "select * from user;";
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
if (rs.next()) {
val1 = rs.getString(2);
val2 = rs.getString(3);
}
value = val1 + " " + val2;
JOptionPane.showMessageDialog(null,value);
}catch(SQLException e){
JOptionPane.showMessageDialog(null, e);
}
}
This only prints the name and the surname of the first user from the table :(
I want to print them all one below another!
If I set rs.getString(5); - it gives me an error: column index out of range.
I suggest you avoid JOptionPane for this kind of code. Better to use some Frame (Swing) and display all of the users into a separate window.
The problem with your code is that variable value is lyiong outside of the loop (which must be btw while loop, as spencer said).
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/mydb","root","");
String sql = "select * from user;";
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
while (rs.next()) {
val = rs.getString(2) + " " + rs.getString(3);
value += val + " ";
}
JOptionPane.showMessageDialog(null,value);
}catch(SQLException e){
JOptionPane.showMessageDialog(null, e);
}
}
Try to avoid this type of code, use ArrayList and save in the array all of the users credentials. Then easily label it wherever you want.
You're only fetching the first row from the resultset. It sounds like you want a loop, and process every row from the resultset.
while (rs.next()) {
}
UPDATE
Q: It only gives me now the last user. Probably because it overwrites the val1 and val2 variable. I suppose somehow this should also goes into the loop..
A: Yes, it should go inside the loop. But I'd be populating a collection, rather than concatenating a String.
As a performance and maintenance note, you can avoid the messiness of the string concatenation in the Java by doing the concatenation in the SQL statement. I wouldn't use SELECT * and rely on the positions of two particular columns in the resultset.
I'd use a SQL statement like this:
SELECT CONCAT(u.first_name,' ',u.last_name) AS user_name FROM users
If I wasn't populating a collection, and I needed to concatenate a honkous string, I'd use a StringBuffer, e.g.
val = new StringBuffer(4096));
while (rs.next()) {
val.append(rs.getString("user_name"));
}
value = val.toString;

Java string interpreted wrongly in SQL

There is an string[] likely;
This array stores the name of column of database table dynamically while runtime.And the program understand the size of the likely in runtime.
Now to put this in sql query .I use a for loop for concatanation of string
for(int k=0;k<likely.length;k++)
{
temp1="\"+likely["+k+"]+\"='Likely' AND ";
temp=temp.concat(temp1);
}
if the size is 3 the final temp will look like
temp = " "+likely[0]+"='Likely' AND "+
likely[1]+"='Likely' AND "+
likely[2]+"='Likely' AND "
Now i formulate sql query as
sql ="SELECT * FROM PUNE WHERE"+temp+"Arts_And_Museum='Yes'";
But during the
ps = con.prepareStatement(sql);
this statement is compiled like
SELECT * FROM PUNE
WHERE [+likely[0]+]='Likely'
AND [+likely[1]+]='Likely'
AND [+likely[2]+]='Likely' AND Arts_And_Museum='Yes'
After deep investigation ,I came to conclusion that it interprets \" as [ or ] alternately..
As a result i get an error
How should i solve this problem?
I run a for loop and prepare a string
I am trying to write a sql syntax
This is why you should use parameterized inputs when dealing with SQL queries.
// conn refers to your database connection
PreparedStatement stmnt = null;
ResultSet rs = null;
try {
stmnt = conn.prepareStatement("SELECT * FROM tbl WHERE col > '?'");
stmnt.setInt(1, 300); //set first parameter to 300
rs = stmnt.executeQuery();
} catch(Exception ex) {
System.err.println("Database exception: " + ex.getMessage());
}
\ is a reserved character. If you want to output a quote in your string you use \".
So, this code:
temp1="\"+likely["+k+"]+\"='Likely' AND ";
Will return this string:
"+likely1]+"='Likely' AND
It seems that your sql is transforming " into [ or ]
The symbol \ is used for escaping. On doing this, you are escaping all the front characters.
Whenever you are asking for an item in array you can access it using likely[k] no need for likey["k"]
Here is how you should do it.
temp1="\\"+likely[k]+"\\='Likely' AND ";
Just change this line:
temp1="\"+likely["+k+"]+\"='Likely' AND ";
To this one:
temp1="\"" + likely[k] + "\"='Likely' AND ";

Java JDBC Retrieve ID After Insert

I use triggers to set PK column values of all tables so i do not do any operation about IDs in java but i need the ID after insert.
How can i get the ID?
stat.execute("INSERT INTO TPROJECT_PROCESS_GROUP(NPROJECT_ID,VDESCRIPTION) " +
"VALUES(" +
"'" + projectID + "'," +
"'" + description + "'" +
"");
Edit: Hi again I read the question, now I get an exception like 'unsupported operation'(i translated from my native language the exact english form might be different). i guess this is about oracle's support for GetGeneratedKeys? Do you know anything about this?
Solution: As mentioned in a book about callablestatements This statement can be used to execute stored procedures and functions. Unlike the PreparedStatement, most databases do not perform any preparation for the call,because it is such a simple command. The CallableStatement instances can be used toreturn the object that the stored procedure—or function, to be more exact—returned.
OracleConnection conn = null;
//OraclePreparedStatement pstat = null;
OracleCallableStatement cstat = null;
String sql = "BEGIN INSERT INTO TPROJECT P (VPROJECT_TITLE,VPROJECT_DESC) VALUES(?,?) RETURNING P.NPROJECT_ID INTO ?; END;";
try {
conn = ConnectionUtility.GetConnection();
cstat = (OracleCallableStatement)conn.prepareCall(sql);
cstat.setString(1, title);
cstat.setString(2, description);
cstat.registerOutParameter(3, OracleTypes.NUMBER);
cstat.execute();
int returnedID = cstat.getInt(3);
// System.out.println(returnedID);
conn.close();
return returnedID;
This example is how you would do it in PostgreSQL. Hopefully you can do something similar in Oracle.
This is how you get the id after INSERT INTO for auto-generated keys like serial . Important here is to provide RETURN_GENERATED_KEYS in the prepareStatement() call.
Resultset result;
PreparedStatement prep;
String query = "INSERT INTO myRel (data) VALUES (?)";
prep = db.prepareStatement(query ,Statement.RETURN_GENERATED_KEYS);
result = prep.getGeneratedKeys();
if(result.next() && result != null){
System.out.println("Key: " + result.getInt(1));
} else {
System.out.println("No, Nop nada");
}
Hope that helps someone :)

Categories