I'm trying to do in my program a JDBC connection with my Embedded H2 database. The problem is that I couldn't execute a simple query with "WHERE ID =". In my databse, the ID are string not integer ('D58BE' in my example).
There is my code :
public Milestone findbyId(String id) throws ClassNotFoundException, SQLException {
Class.forName("org.h2.Driver");
Connection connection = DriverManager.getConnection("jdbc:h2:~/dao_db", "sa", "");
PreparedStatement prepareStatement = connection.prepareStatement("SELECT * FROM MILESTONE WHERE ID= 'D58BE'");
The problem is that the SAME query ("SELECT * FROM MILESTONE WHERE ID= 'D58BE'") works perfectly in my embeded database (I verify the result with the h2.jar provided to manage the database). While in eclipse, I had this exception :
Exception in thread "main" org.h2.jdbc.JdbcSQLException: Column "D58BE" not found [42122-191]
I tried A LOT of things but it still never works...
To execute it directly, create a statement and execute your SQL:
Statement statement = connection.createStatement();
statement.executeQuery("SELECT * FROM MILESTONE WHERE ID= 'D58BE'");
You are using a prepared statement, so you need to use placeholders:
PreparedStatement statement = connection.prepareStatement("SELECT * FROM MILESTONE WHERE ID=?);
statement.setString(1, "D58BE");
statement.executeQuery();
EDIT
For a detailed example and also how to process a ResultSet, you can look at the following tutorial: https://docs.oracle.com/javase/tutorial/jdbc/basics/processingsqlstatements.html
For your case it should be something along the lines of:
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String id = rs.getString("ID");
String name = rs.getNamex("NAME"); // Assuming there is a column called name.
System.out.println(id);
}
Related
The query inside MySQL is working:
DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'
I can delete data inside MySQL, but the problem is whenever I try to remove the account_tags from my Java application, it throws an error:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELETE FROM f9.yoo
WHERE account_tags = '#8GGGJPUR9'' at line 2
Here's my Java SQL query:
Statement statement = dbConnection.createStatement();
String sql = "SELECT * FROM "+databaseName+"."+tableName+";\n" +
"DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = '"+AccountTag+"';";
statement.executeQuery(sql);
The error isn't giving me much to work with, so I really have no idea what is wrong with the program.
Did you add the allowMultiQueries=true
If not then you can add that while you sending the connecting request to your database. So you need to append the allowMultiQueries=true in your to database URL.
Like this:
String dbUrl = "jdbc:mysql:///test?allowMultiQueries=true";
String sql = "DELETE FROM "+databaseName+"."+tableName+"\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
int updateCount = statement.executeUpdate();
System.out.printf("%s: %d records deleted.%n", tableName, updateCount);
}
The only thing used is the DELETE, for which one should use executeUpdate.
One definitely should use a PreparedStatement as many code checkers will give alarms otherwise. It escapes things like ', handles types of the arguments, and possible conversions, and especially is a security feature against SQL injection.
The System.out usage is bad style, better would be using a logger.
try-with-resources automatically closes the PreparedStatement even with a raised exception or break/return.
When doing both database operations, it seems better to use two (prepared) statements, as the first returns a ResultSet.
So:
String sql = SELECT * FROM "+databaseName+"."+tableName + "\n" +
"WHERE account_tags = ?";
try (PreparedStatement statement = dbConnection.prepareStatement(sq)) {
statement.setString(1, AccountTag);
try (ResultSet rs = statement.executeQuery()) {
...
}
}
Better to separate statements with an If condition :
String sql1="SELECT * FROM "+databaseName+"."+tableName;
String sql2="DELETE FROM "+databaseName+"."+tableName+" "+
"WHERE account_tags = '"+AccountTag+"';
statement.executeQuery(sql1);
statement.executeUpdate(sql2);
In ORACLE(11g), in some package I have a function that returning table:
SELECT * FROM TABLE( my_PKG.fnc_myList());
So it works perfectly in Oracle SQL Developer tool, for example. I got rows from the target table in Query Result of SQL Developer.
Question:
Will it work from JAVA (8) code?
I tried the code below:
con = DriverManager.getConnection(...);
String SQLQ = "{SELECT * FROM TABLE( my_PKG.fnc_myList());}";
Statement st =con.createStatement();
rs=st.executeQuery(SQLQ);
while (rs.next()) {
int id = rs.getInt(0);
String name = rs.getString(1);
....
}
But got the error:
java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement
Am I wrong somehwere else or it couldn't work at all through JDBC driver?
You should neither use the braces nor the semi-colon. The braces are sometimes used if only a stored procedure is called. But you have a SELECT statement (even if it contains a function). And the semi-colon is only used in PL/SQL or in tools like SQL developer to separate statements:
con = DriverManager.getConnection(...);
String SQLQ = "SELECT * FROM TABLE( my_PKG.fnc_myList())";
Statement st =con.createStatement();
rs=st.executeQuery(SQLQ);
while (rs.next()) {
int id = rs.getInt(0);
String name = rs.getString(1);
....
}
A security scan made by AppScan source flags that the input has to be validated (Validation.Required) on the line uprs.updateString in the code below:
PreparedStatement statement =
conn.prepareStatement (query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
...
ResultSet uprs = statement.executeQuery ();
...
// Update DB ColumnA with input coming from client
uprs.updateString ('ColumnA', unvalidatedUserInput);
...
// Updates the underlying database
uprs.updateRow();
I assume that the intention behind this is to avoid SQL injection attacks, but I'm not sure whether that is possible in that scenario.
Questions: Are SQL Injection attacks possible through these JDBC methods? How does JDBC implements this under the scenes? Would this be another false positive reported by AppScan?
I'm not sure about bluemix-app-scan, but I'm providing my explanation here. (This is my assumption based on the below tests and code pasted)
I ran a test code to check this (in H2 DB)
value of testName String : (select 'sqlInjection' from dual)
Using createStatement (Not-Safe):
String query = "update TEST_TABLE set TEST_CHAR = " + testName + " where ID = 1";
Statement statement = connection.createStatement();
statement.executeUpdate(query);
Output: TEST_CHAR in DB was sqlInjection.
Using ResultSet of createStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet executeQuery = statement.executeQuery(query);
executeQuery.next();
executeQuery.updateString("TEST_CHAR", testName);
executeQuery.updateRow();
Output: Surprisingly TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using PreparedStatement (Safe):
String query = "update TEST_TABLE set TEST_CHAR = ? where ID = 1";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, testName);
statement.executeUpdate();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using ResultSet of prepareStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = statement.executeQuery();
uprs.next();
uprs.updateString("TEST_CHAR", testName);
uprs.updateRow();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Back to Questions:
Are SQL Injection attacks possible through these JDBC methods?
Maybe. It depends on the database driver that you're using.
How? :
The reason SQL Injection failed in my result set update was because H2 database internally uses PreparedStatement to update the row when ResultSet.updateRow() is invoked.
public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StatementBuilder buff = new StatementBuilder("UPDATE ");
...
buff.append(" SET ");
appendColumnList(buff, true);
...
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
...
for (int i = 0; i < columnCount; i++) {
...
v.set(prep, j++);
}
setKey(prep, j, current);
int count = prep.executeUpdate();
...
}
I'm not sure if all DB drivers in java implemented updateRow() method using preparedStatement or not. However it's clear that this is left to the driver and if bluemix is suggesting you to add a validation here, I suggest you follow that :)
How does JDBC implements this under the scenes?
Well, as shown above this is driver specific. However there is a good explanation on how PreparedStatement handles it over here.
Would this be another false positive reported by AppScan?
I don't think this is false positive (but in cases like H2 DB it is) but you'll never know if all database drivers implemented this securely.
Edit -
Even PostgreSQL and MySQL use PreparedStatement to handle this.
public synchronized void updateRow() throws SQLException
{
...
updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
...
updateStatement.executeUpdate();
...
}
I'm trying to get some data from Oracle 11.2 using java and jdbc driver.
My goal is to get data from database using CallableStatement, but with no luck - I'm not able to put table name as parameter. I would like to have configurable table name in query. However, it would be good to keep it sanitized.
Here is an example..
public void getData() throws SQLException {
Connection conn = Config.getSQLConnection();
String query = "SELECT * FROM ?";
PreparedStatement st = conn.prepareStatement(query);
st.setString(1, Config.DATATABLE_NAME);
ResultSet rs = st.executeQuery();
if (rs.next()) {
System.out.println("SUCCESS");
System.out.println("ID:" + rs.getString("ID"));
} else {
System.out.println("FAILURE");
}
}
Is this the way it should work? Or am I missing something, or misused it?
A CallableStatement is used to make call to stored procedures.
From javadoc:
The interface used to execute SQL stored procedures
Use a PreparedStament instead for a normal select.
As an additional note don't pass the name of the table as parameter.
Create the query using concatenation.
Instead of
String query = "SELECT * FROM ?";
use
String query = "SELECT * FROM " + Config.DATATABLE_NAME;
You should use PreparedStatement instead of CallableStatement.
CallableStatement is an interface which is used to call stored procedures.
i have the below code, where I'm inserting records to a table. When I try to get resultset, it returns null. How to get the latest added row into a resultset?
String sql1 = "INSERT INTO [xxxx].[dbo].[xxxxxx](WORKFLOW_SEQ_NBR," +
" WORKFLOW_LOG_TYPE_CODE, WORKFLOW_STATUS_CODE, DISP_CODE, DISP_USER, DISP_COMMENT, DISP_TITLE, DISP_TS)" +
"VALUES(?,?,?,?,?,?,?,?)";
PreparedStatement pst = connect.prepareStatement(sql1);
pst.setString(1, ...);
pst.setString(2, ...);
...
...
...
pst.executeUpdate();
ResultSet rstest = pst.executeQuery();
// ResultSet rstest = pst.getResultSet();
EDIT: Resolved
added following method to go to the last added row
st.execute("Select * from [xxxx].[dbo].[xxxxxxxxx]");
ResultSet rstest = st.getResultSet();
rstest.afterLast();
GETLASTINSERTED:
while(rstest.previous()){
System.out.println(rstest.getObject(1));
break GETLASTINSERTED;//to read only the last row
}
When using a SQL statement such as INSERT, UPDATE or DELETE with a PreparedStatement, you must use executeUpdate, which will return the number of affeted rows. In this case there is simply no ResultSet produced by the sql operation and thus calling executeQuery will throw a SQLException.
If you actually need a ResultSet you must make another statement with a SELECT SQL operation.
See the javadoc for PreparedStatement#executeQuery and PreparedStatement#executeUpdate
Seems like this is an older question, but i'm looking for a similar solution, so maybe people will still need this.
If you're doing an insert statement, you can use the :
Connection.PreparedStatement(String, String[]) constructor, and assign those to a ResultSet with ps.getGeneratedKeys().
It would look something like this:
public void sqlQuery() {
PreparedStatement ps = null;
ResultSet rs = null;
Connection conn; //Assume this is a properly defined Connection
String sql = "insert whatever into whatever";
ps = conn.prepareStatement(sql, new String[]{"example"});
//do anything else you need to do with the preparedStatement
ps.execute;
rs = ps.getGeneratedKeys();
while(rs.next()){
//do whatever is needed with the ResultSet
}
ps.close();
rs.close();
}
Connection#prepareStatement() - Creates a PreparedStatement object for sending parameterized SQL statements to the database.
which means connect.prepareStatement(sql1); created the PreparedStatement object using your insert query.
and when you did pst.executeUpdate(); it will return the row count for SQL Data Manipulation Language (DML) statements or 0 for SQL statements that return nothing
Now if you again want to fetch the data inserted you need to create a new PreparedStatement object with Select query.
PreparedStatement pstmt = connect.prepareStatement("SELECT * FROM tableName");
then this shall give you the ResultSet object that contains the data produced by the query
ResultSet rstest = pstmt.executeQuery();