This question already has answers here:
Using a variable instead of a parameter index with a JDBC prepared statement
(3 answers)
Closed 2 years ago.
Are there named parameters in JDBC instead of positional ones, like the #name, #city in the ADO.NET query below?
select * from customers where name=#name and city = #city
JDBC does not support named parameters. Unless you are bound to using plain JDBC (which causes pain, let me tell you that) I would suggest to use Springs Excellent JDBCTemplate which can be used without the whole IoC Container.
NamedParameterJDBCTemplate supports named parameters, you can use them like that:
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValue("name", name);
paramSource.addValue("city", city);
jdbcTemplate.queryForRowSet("SELECT * FROM customers WHERE name = :name AND city = :city", paramSource);
To avoid including a large framework, I think a simple homemade class can do the trick.
Example of class to handle named parameters:
public class NamedParamStatement {
public NamedParamStatement(Connection conn, String sql) throws SQLException {
int pos;
while((pos = sql.indexOf(":")) != -1) {
int end = sql.substring(pos).indexOf(" ");
if (end == -1)
end = sql.length();
else
end += pos;
fields.add(sql.substring(pos+1,end));
sql = sql.substring(0, pos) + "?" + sql.substring(end);
}
prepStmt = conn.prepareStatement(sql);
}
public PreparedStatement getPreparedStatement() {
return prepStmt;
}
public ResultSet executeQuery() throws SQLException {
return prepStmt.executeQuery();
}
public void close() throws SQLException {
prepStmt.close();
}
public void setInt(String name, int value) throws SQLException {
prepStmt.setInt(getIndex(name), value);
}
private int getIndex(String name) {
return fields.indexOf(name)+1;
}
private PreparedStatement prepStmt;
private List<String> fields = new ArrayList<String>();
}
Example of calling the class:
String sql;
sql = "SELECT id, Name, Age, TS FROM TestTable WHERE Age < :age OR id = :id";
NamedParamStatement stmt = new NamedParamStatement(conn, sql);
stmt.setInt("age", 35);
stmt.setInt("id", 2);
ResultSet rs = stmt.executeQuery();
Please note that the above simple example does not handle using named parameter twice. Nor does it handle using the : sign inside quotes.
Vanilla JDBC only supports named parameters in a CallableStatement (e.g. setString("name", name)), and even then, I suspect the underlying stored procedure implementation has to support it.
An example of how to use named parameters:
//uss Sybase ASE sysobjects table...adjust for your RDBMS
stmt = conn.prepareCall("create procedure p1 (#id int = null, #name varchar(255) = null) as begin "
+ "if #id is not null "
+ "select * from sysobjects where id = #id "
+ "else if #name is not null "
+ "select * from sysobjects where name = #name "
+ " end");
stmt.execute();
//call the proc using one of the 2 optional params
stmt = conn.prepareCall("{call p1 ?}");
stmt.setInt("#id", 10);
ResultSet rs = stmt.executeQuery();
while (rs.next())
{
System.out.println(rs.getString(1));
}
//use the other optional param
stmt = conn.prepareCall("{call p1 ?}");
stmt.setString("#name", "sysprocedures");
rs = stmt.executeQuery();
while (rs.next())
{
System.out.println(rs.getString(1));
}
You can't use named parameters in JDBC itself. You could try using Spring framework, as it has some extensions that allow the use of named parameters in queries.
Plain vanilla JDBC does not support named parameters.
If you are using DB2 then using DB2 classes directly:
Using named parameter markers with PreparedStatement objects
Using named parameter markers with CallableStatement objects
EDIT: New links for Db2 v11.5:
Using named parameter markers with PreparedStatement objects
Using named parameter markers with CallableStatement objects
Related
I'm trying to use prepared statements to set a table name to select data from, but I keep getting an error when I execute the query.
The error and sample code is displayed below.
[Microsoft][ODBC Microsoft Access Driver] Parameter 'Pa_RaM000' specified where a table name is required.
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [?]"; //?=date
public Execute(String reportDate){
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(Display.DB_MERC);
PreparedStatement st = conn.prepareStatement(query1);
st.setString(1, reportDate);
ResultSet rs = st.executeQuery();
Any thoughts on what might be causing this?
A table name can't be used as a parameter. It must be hard coded. So you can do something like:
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [" + reportDate + "?]";
If you need a solution which is not vulnerable to SQL injection, you have to duplicate the query for all tables you need:
final static String QUERIES = {
"SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ...",
...
};
And yes: the queries are duplicates and only the table name differs.
Now you simply select the query that fits your table, e.g. like
...
PreparedStatement st = conn.prepareStatement(QUERIES[index]);
...
You can use this approach wich JPA, Hibernate, whatever...
If you want a more verbose approach consider using an enum like
enum AQuery {
Table1("SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ..."),
Table2("SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ..."),
Table3("SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ..."),
...
private final String query;
AQuery(final String query) {
this.query = query;
}
public String getQuery() {
return query;
}
}
Now use the either an index
String sql = AQuery.values()[index].getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
Or use a table name
String sql = AQuery.valueOf("Table1").getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
This is technically possible with a workaround, but very bad practice.
String sql = "IF ? = 99\n";
sql += "SELECT * FROM first_table\n";
sql += "ELSE\n";
sql += "SELECT * FROM second_table";
PreparedStatement ps = con.prepareStatement(sql);
And then when you want to select from first_table you set the parameter with
ps.setInt(1, 99);
Or if not, you set it to something else.
As a number of people have said, you can't use a statement parameter for a table name, only for variables as part of the condition.
Based on the fact you have a variable table name with (at least) two table names, perhaps it would be best to create a method which takes the entity you are storing and returns a prepared statement.
PreparedStatement p = createStatement(table);
You can't set table name in prepared statement
As said before, it is not possible to set the table name in a prepared statement with preparedStatement.setString(1, tableName). And it is also not possible to add parts of the SQL query to a prepared statement (eg preparedStatement.addSql(" or xyz is null")).
How to do it right without risking SQL injections?
The table name must be inserted into the SQL (or JQL) query you want to execute with string operations like "select * from " + tableName or String.format("select * from %s", tableName)
But how to avoid SQL injections?
If the table name does not come from user input, you are probably safe.
For example, if you make a decision like here
String tableName;
if(condition) {
tableName = "animal";
} else {
tableName = "plant";
}
final String sqlQuery = "delete from " + tableName;
...
If the table name depends on the users input, you need to check the input manually.
For example, with a white-list containing all valid table names:
if(!tableNamesWhitelist.contains(tableName)) {
throw new IllegalArgumentException(tableName + " is not a valid table name");
}
String sqlQuery = "delete from " + tableName;
or with an enum:
public enum Table {
ANIMAL("animal"),
PLANT("plant");
private sqlTableName;
private TableName(String sqlTableName) {
this.sqlTableName= sqlTableName;
}
public getSqlTableName() {
return sqlTableName;
}
}
and then convert the user-input string like ANIMAL into Table.ANIMAL. An exception is thrown, if no fitting enumeration value does exist.
eg
#DeleteMapping("/{table}")
public String deleteByEnum(#PathVariable("table") Table table) {
final String sqlQuery = "delete from " + table.getSqlTableName();
...
}
Of course these examples work with select, update, ... too and a lot of other implementations to check the user input are possible.
This might help:
public ResultSet getSomething(String tableName) {
PreparedStatement ps = conn.prepareStatement("select * from \`"+tableName+"\`");
ResultSet rs = ps.executeQuery();
}
I'm not sure you can use a PreparedStatement to specify the name of the table, just the value of some fields. Anyway, you could try the same query but, without the brackets:
"SELECT plantID, edrman, plant, vaxnode FROM ?"
String table="pass";
String st="select * from " + table + " ";
PreparedStatement ps=con.prepareStatement(st);
ResultSet rs = ps.executeQuery();
Sorry if the title is not precise.
I am using a custom class to get data from a SQLite database.
For example:
the method below is supposed to return list of users, which are members of a certain department.
Each user in the USER table has a column with id of the department he belongs to.
At the moment I am getting all the users and then comparing their department IDs to the targetID of the department I am looking for.
Is there a way to get just the set of users that have a particular department ID, so that I don't have to check each one's department id?
private List<User> getDepartmentMembers(int targetID) {
List<User> members = new ArrayList<User>();
Connection c = null;
Statement statement = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:TheatroData.sqlite");
c.setAutoCommit(false);
statement = c.createStatement();
ResultSet rs = statement.executeQuery( "SELECT * FROM USERS;" );
while ( rs.next() ) {
int id = rs.getInt(Constants.ID_KEY);
if (id == targetID ){
User tmp = null;
int position = rs.getInt(Constants.POSITION_KEY);
if (position == Constants.DEPARTMENT_HEAD)
tmp = new DepartmentHead();
else if (position == Constants.DEPARTMENT_MANAGER)
tmp = new DepartmentManager();
else if (position == Constants.DEPARTMENT_MEMBER);
tmp = new GruntUser();
tmp.setID(id);
tmp.setName(rs.getString(Constants.NAME_KEY));
tmp.setPosition(position);
tmp.setUsername(rs.getString(Constants.USERNAME_KEY));
tmp.setLastname(rs.getString(Constants.SURNAME_KEY));
tmp.setDepartment(targetID);
tmp.setPassword(rs.getString(Constants.PASS_KEY));
members.add(tmp);
}
}
rs.close();
statement.close();
c.close();
} catch ( Exception e ) {
System.err.println( e + " -in getDepartmentMembers" + e.getClass().getName() + ": " + e.getMessage());
}
return members;
}
I was thinking I need something like this:
ResultSet rs = statement.executeQuery( "SELECT * FROM USERS where department = ?;", targetID );
In an ideal world, you could do it as you wrote:
ResultSet rs = statement.executeQuery( "SELECT * FROM USERS WHERE department = ?;", targetID );
But, executeQuery from JDBC does currently not provide the possibility for argument binding. So you have to use "Prepared Statements".
Instead of
statement = c.createStatement();
ResultSet rs = statement.executeQuery( "SELECT * FROM USERS;" );
do:
prepared = c.prepareStatement("SELECT * FROM USERS WHERE department = ?;");
prepared.setString(1, targetID);
ResultSet rs = prepared.executeQuery();
When you need more than one parameter, you can use a different syntax for replacing it, for example "?001". See SQLite Documentation: C/C++ Interface Section 5.
Also remove the Java coding for your own selection of the right department.
Since the CluelessStudent presented a different solution, involving string concatenation, I want to say the following:
I would definitively discourage string concatenation! You always
should use argument binding and not string concatenation! String
concatenation is a huge security risk, since it can be used for so
called "SQL injection attacks". See Wikipedia: SQL Injection
Yes you pratcially answered your own question. You can also do like this.
String query = "SELECT * FROM USERS where department = (?)";
PreparedStatement statement = c.prepareStatement(sql);
statement.setInt(1, targetId);
ResultSet rs = statement.executeQuery();
while(rs.next()){
//you get only records that have id = targetId
}
//close rs, statement and connection!!!
I was just passing a wrong statement. The correct way:
ResultSet rs = statement.executeQuery( "SELECT * FROM USERS where department = "+targetID+";");
I have a general Java method with the following method signature:
private static ResultSet runSQLResultSet(String sql, Object... queryParams)
It opens a connection, builds a PreparedStatement using the sql statement and the parameters in the queryParams variable length array, runs it, caches the ResultSet (in a CachedRowSetImpl), closes the connection, and returns the cached result set.
I have exception handling in the method that logs errors. I log the sql statement as part of the log since it's very helpful for debugging. My problem is that logging the String variable sql logs the template statement with ?'s instead of actual values. I want to log the actual statement that was executed (or tried to execute).
So... Is there any way to get the actual SQL statement that will be run by a PreparedStatement? (Without building it myself. If I can't find a way to access the PreparedStatement's SQL, I'll probably end up building it myself in my catches.)
Using prepared statements, there is no "SQL query" :
You have a statement, containing placeholders
it is sent to the DB server
and prepared there
which means the SQL statement is "analysed", parsed, some data-structure representing it is prepared in memory
And, then, you have bound variables
which are sent to the server
and the prepared statement is executed -- working on those data
But there is no re-construction of an actual real SQL query -- neither on the Java side, nor on the database side.
So, there is no way to get the prepared statement's SQL -- as there is no such SQL.
For debugging purpose, the solutions are either to :
Ouput the code of the statement, with the placeholders and the list of data
Or to "build" some SQL query "by hand".
It's nowhere definied in the JDBC API contract, but if you're lucky, the JDBC driver in question may return the complete SQL by just calling PreparedStatement#toString(). I.e.
System.out.println(preparedStatement);
At least MySQL 5.x and PostgreSQL 8.x JDBC drivers support it. However, most other JDBC drivers doesn't support it. If you have such one, then your best bet is using Log4jdbc or P6Spy.
Alternatively, you can also write a generic function which takes a Connection, a SQL string and the statement values and returns a PreparedStatement after logging the SQL string and the values. Kickoff example:
public static PreparedStatement prepareStatement(Connection connection, String sql, Object... values) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
logger.debug(sql + " " + Arrays.asList(values));
return preparedStatement;
}
and use it as
try {
connection = database.getConnection();
preparedStatement = prepareStatement(connection, SQL, values);
resultSet = preparedStatement.executeQuery();
// ...
Another alternative is to implement a custom PreparedStatement which wraps (decorates) the real PreparedStatement on construction and overrides all the methods so that it calls the methods of the real PreparedStatement and collects the values in all the setXXX() methods and lazily constructs the "actual" SQL string whenever one of the executeXXX() methods is called (quite a work, but most IDE's provides autogenerators for decorator methods, Eclipse does). Finally just use it instead. That's also basically what P6Spy and consorts already do under the hoods.
I'm using Java 8, JDBC driver with MySQL connector v. 5.1.31.
I may get real SQL string using this method:
// 1. make connection somehow, it's conn variable
// 2. make prepered statement template
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO oc_manufacturer" +
" SET" +
" manufacturer_id = ?," +
" name = ?," +
" sort_order=0;"
);
// 3. fill template
stmt.setInt(1, 23);
stmt.setString(2, 'Google');
// 4. print sql string
System.out.println(((JDBC4PreparedStatement)stmt).asSql());
So it returns smth like this:
INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
If you're executing the query and expecting a ResultSet (you are in this scenario, at least) then you can simply call ResultSet's getStatement() like so:
ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();
The variable executedQuery will contain the statement that was used to create the ResultSet.
Now, I realize this question is quite old, but I hope this helps someone..
I've extracted my sql from PreparedStatement using preparedStatement.toString() In my case toString() returns String like this:
org.hsqldb.jdbc.JDBCPreparedStatement#7098b907[sql=[INSERT INTO
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]
Now I've created a method (Java 8), which is using regex to extract both query and values and put them into map:
private Map<String, String> extractSql(PreparedStatement preparedStatement) {
Map<String, String> extractedParameters = new HashMap<>();
Pattern pattern = Pattern.compile(".*\\[sql=\\[(.*)],\\sparameters=\\[(.*)]].*");
Matcher matcher = pattern.matcher(preparedStatement.toString());
while (matcher.find()) {
extractedParameters.put("query", matcher.group(1));
extractedParameters.put("values", Stream.of(matcher.group(2).split(","))
.map(line -> line.replaceAll("(\\[|])", ""))
.collect(Collectors.joining(", ")));
}
return extractedParameters;
}
This method returns map where we have key-value pairs:
"query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)"
"values" -> "value, value, value"
Now - if you want values as list you can just simply use:
List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(","))
.collect(Collectors.toList());
If your preparedStatement.toString() is different than in my case it's just a matter of "adjusting" regex.
Using PostgreSQL 9.6.x with official Java driver 42.2.4:
...myPreparedStatement.execute...
myPreparedStatement.toString()
Will show the SQL with the ? already replaced, which is what I was looking for.
Just added this answer to cover the postgres case.
I would never have thought it could be so simple.
Code Snippet to convert SQL PreparedStaments with the list of arguments. It works for me
/**
*
* formatQuery Utility function which will convert SQL
*
* #param sql
* #param arguments
* #return
*/
public static String formatQuery(final String sql, Object... arguments) {
if (arguments != null && arguments.length <= 0) {
return sql;
}
String query = sql;
int count = 0;
while (query.matches("(.*)\\?(.*)")) {
query = query.replaceFirst("\\?", "{" + count + "}");
count++;
}
String formatedString = java.text.MessageFormat.format(query, arguments);
return formatedString;
}
Very late :) but you can get the original SQL from an OraclePreparedStatementWrapper by
((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
I implemented the following code for printing SQL from PrepareStatement
public void printSqlStatement(PreparedStatement preparedStatement, String sql) throws SQLException{
String[] sqlArrya= new String[preparedStatement.getParameterMetaData().getParameterCount()];
try {
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(sql);
StringBuffer sb = new StringBuffer();
int indx = 1; // Parameter begin with index 1
while (matcher.find()) {
matcher.appendReplacement(sb,String.valueOf(sqlArrya[indx]));
}
matcher.appendTail(sb);
System.out.println("Executing Query [" + sb.toString() + "] with Database[" + "] ...");
} catch (Exception ex) {
System.out.println("Executing Query [" + sql + "] with Database[" + "] ...");
}
}
If you're using MySQL you can log the queries using MySQL's query log. I don't know if other vendors provide this feature, but chances are they do.
Simply function:
public static String getSQL (Statement stmt){
String tempSQL = stmt.toString();
//please cut everything before sql from statement
//javadb...:
int i1 = tempSQL.indexOf(":")+2;
tempSQL = tempSQL.substring(i1);
return tempSQL;
}
It's fine aswell for preparedStatement.
I'm using Oralce 11g and couldn't manage to get the final SQL from the PreparedStatement. After reading #Pascal MARTIN answer I understand why.
I just abandonned the idea of using PreparedStatement and used a simple text formatter which fitted my needs. Here's my example:
//I jump to the point after connexion has been made ...
java.sql.Statement stmt = cnx.createStatement();
String sqlTemplate = "SELECT * FROM Users WHERE Id IN ({0})";
String sqlInParam = "21,34,3434,32"; //some random ids
String sqlFinalSql = java.text.MesssageFormat(sqlTemplate,sqlInParam);
System.out.println("SQL : " + sqlFinalSql);
rsRes = stmt.executeQuery(sqlFinalSql);
You figure out the sqlInParam can be built dynamically in a (for,while) loop I just made it plain simple to get to the point of using the MessageFormat class to serve as a string template formater for the SQL query.
You can try to use javaagent to print SQL:
public class Main {
private static final String mybatisPath = "org.apache.ibatis.executor.statement.PreparedStatementHandler";
private static final String mybatisMethod = "parameterize";
private static final String sqlPath = "java.sql.Statement";
public static void premain(String arg, Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassFileTransformer() {
#Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (!mybatisPath.replaceAll("\\.", "/").equals(className)) {
return null;
}
ClassPool pool = new ClassPool();
pool.appendClassPath(new LoaderClassPath(loader));
pool.appendSystemPath();
try {
CtClass ctClass = pool.get(mybatisPath);
CtMethod method = ctClass.getDeclaredMethod(mybatisMethod, new CtClass[]{pool.get(sqlPath)});
method.insertAfter("cn.wjhub.Main#printSQL($1)");
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
/**
* printSQL
*
* #param statement statement
*/
private void printSQL(Statement statement) {
String sqlSource = statement.toString();
System.out.println(sqlSource);
}
}
To do this you need a JDBC Connection and/or driver that supports logging the sql at a low level.
Take a look at log4jdbc
I'm converting a Java application from PostGresSQL to Derby (10.10.1.1). The PG database has many procedures that ideally will transfer to Derby procedures.
One of the PG stored procedures passes an array of Timestamps, similar to this Procedure/SQL:
CREATE FUNCTION getDownloads(_download_array timestamp without time zone[])
LANGUAGE plpgsql AS $$
DECLARE mycurs refcursor;
BEGIN
SELECT * FROM download_time d
WHERE d.downloadtime = ANY(_download_array);
END
RETURN mycurs;
Derby procedures are basically declarations that reference your procedures class that contains public static Java methods. The methods typically use the java.SQL PreparedStatement object, and may contain dynamic parameters. The procedure is called via the java.SQL CallableStatement object, with set param values, executed to return a ResultSet.
I would like to translate the above PG procedure into a Derby procedure that accepts multiple Timestamp values, possibly using the ANY or IN statements. In limited searches, it appears that Derby does not support arrays as dynamic parameters.
Using the Squirrel SQL client, this syntax proves acceptable:
SELECT * FROM download_time d
WHERE d.downloadtime
IN('2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0', '2014-05-06 07:08:09.0')
However in practice, passing comma-delimited Timestamps to the IN or ANY statements does not work, pseudo-code below:
try {
Connection conn = getConnection();
CallableStatement cstmt = null;
cstmt = conn.prepareCall("{ call getDownloads(?) }");
cstmt.setTimestamp(3, "'2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0'");
//Also tried this:
cstmt.setString(3, "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0");
cstmt.execute();
rs = cstmt.getResultSet();
while (null != rs && rs.next()) {
...
}
} catch (SQLException sqle) {
...handle errors
}
Following the above examples, this error occurs:
java.sql.SQLException:
The syntax of the string representation of a date/time value is incorrect.
I'm in search of alternative methods, and am considering solutions I've found in an excellent article on StackOverflow, PreparedStatement IN clause alternatives?
I would be willing to consider simply writing dynamic SQL instead of a parameterized procedure, but the real query is rather beastly. :)
Since no one offered an answer, I'm posting my solution to the problem. The solution is to pass a String variable, "downloadTimes" containing concatenated date/times in a comma-delimited-like format. For brevity, the NULL-check condition was excluded. If a NULL is passed, that line is simply excluded.
Here is the procedure:
public static void getDownloads(int theId, String downloadTimes, ResultSet[] rs)
throws SQLException {
String DML = null;
PreparedStatement ps = null;
DML = "SELECT d.* FROM download_time d WHERE d.id = ? " +
"AND d.downloadtime IN(" + downloadTimes + ") " : "") + //Add chk null condition
"ORDER BY 1, 2 DESC, 3 ";
ps = conn.prepareStatement(DML);
ps.setInt(1, theId);
rs[0] = ps.executeQuery();
}
Note that the "getDownloads" procedure is declared in Derby later in the same class (see declaration in my original question), left out for simplicity. The procedure is called by a method in a different class:
public Map<GregorianCalendar, List<Fault>> getDownloadFaultList(
Integer theId, String subsystem, List<GregorianCalendar> downloadTimes) {
CallableStatement cstmt = null;
ResultSet rs = null;
String downloadCalListToCsv = null;
// parseGregorianCalListToCsv() creates a CSV string out of dates.
// I.e., "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0"
if (false == downloadTimes.isEmpty()) {
downloadCalListToCsv = DataTypeConverter
.parseGregorianCalListToCsv(downloadTimes, timestampFormat);
}
try {
cstmt = getConn().prepareCall("{ call getDownloads(?, ?) }");
// Register the parameters
cstmt.setInt(1, theId);
// Get timezone from first entry, assuming all same timezone
if (! downloadTimes.isEmpty()) {
cal.setTimeZone(downloadTimes.get(0).getTimeZone());
}
cstmt.setString(2, downloadCalListToCsv);
cstmt.execute();
rs = cstmt.getResultSet();
while (null != rs && rs.next()) {
//Use the download timestamps here
}
} catch (SQLException sqle) {
//error handling here
} finally {
//Close resources
close(rs, cstmt);
}
return faultMap;
}
The solution is not elegant, but works in practice.
I have been searching and trying different stuff for awhile, but have not found an answer. I'm trying to make a connection to sql using JDBC from eclipse. I am having trouble when I need to select a string in the database. If I use:
Select name from data where title = 'mr';
That works with terminal/command line but when I try to use eclipse where I use
statement sp = connection.createstatement();
resultset rs = sp.executequery("select name from data where title = '" + "mr" + "'");
It does not give me anything while the terminal input does. What did I do wrong in the eclipse? Thanks
Heres a part of the code. Sorry, its a bit messy, been trying different things.
private boolean loginChecker(String cid, String password) throws SQLException{
boolean check = false;
PreparedStatement pstatment = null;
Statement stmt = null;
//String query = "SELECT 'cat' FROM customer";
String query = "select '"+cid+"' from customer where password = '"+password+"'";
try {
System.out.println("in try......");
//stmt = con.createStatement();
//ResultSet rs = stmt.executeQuery(query);
PreparedStatement prepStmt = con.prepareStatement(query);
ResultSet rs = prepStmt.executeQuery();
//System.out.print(rs.getString("cid"));
while(rs.next()){
check = true;
System.out.print(rs.getString("cid"));
}
} catch (SQLException e ) {
e.printStackTrace();
} finally {
if (stmt != null) {
//stmt.close();
}
}
return check;
}
Second try on a simpler query:
public List<Object> showTable() {
List<Object> result = new ArrayList<Object>();
String name = "bob";
try
{
PreparedStatement preStatement = con.prepareStatement("select total from test where name = ?");
preStatement.setString(1, name);
ResultSet rs1 = preStatement.executeQuery();
while(rs1.next()){
System.out.println("there");
System.out.println(rs1.getInt("total"));
}
}
catch (SQLException ex)
{
System.out.print("Message: " + ex.getMessage());
}
return result;
}
Remove the quotes around the column name.
String query = "select "+cid+" from customer where password = '"+password+"'";
You've not mentioned which database you're working with but many databases like Oracle change the column case to upper case unless they're quoted. So, you only quote table columns if that's how you had created them. For example, if you had created a table like
CREATE TABLE some_table ( 'DoNotChangeToUpperCase' VARCHAR2 );
Then you would have to select the column with quotes as well
SELECT 'DoNotChangeToUpperCase' FROM some_table
But, if you didn't create the table using quotes you shouldn't be using them with your SELECTs either.
Make sure you are not closing the ResultSet before you are trying to use it. This can happen when you return a ResultSet and try to use it elsewhere. If you want to return the data like this, use CachedRowSet:
CachedRowSet crs = new CachedRowSetImpl();
crs.populate(ResultSet);
CachedRowSet is "special in that it can operate without being connected to its data source, that is, it is a disconnected RowSet object"
Edit: Saw you posted code so I thought I add some thoughts. If that is your ACTUAL code than the reason you are not getting anything is because the query is probably not returning anything.
String query = "select '"+cid+"' from customer where password = '"+password+"'";
This is wrong, for two reasons. 1) If you are using prepared statements you should replace all input with '?' so it should look like the following:
String query = "select name from customer where password = ?";
Then:
PreparedStatement prepStmt = con.prepareStatement(query);
prepStmt.setString(1, password);
ResultSet rs = prepStmt.executeQuery();
2)
System.out.print(rs.getString("cid"));
Here are are trying to get the column named "cid", when it should be the name stored in cid. You should actually never be letting the user decide what columns to get, this should be hardcoded in.