Avoiding try-catch-finally hell - java

let say I have simple connection and select from database. Sadness part of this code is that almost let say 40-50% is try-catch boilerplate code. How can I deal with it?
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root"
);
statement = connection.createStatement();
String sql = "SELECT id, name from user";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
rs.close();
} catch (Exception se) {
se.printStackTrace();
}
finally {
try {
if (statement != null)
statement.close();
} catch (SQLException ignored) {
}
try {
if (connection != null)
connection.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
}

The way to avoid try-catch-finally hell is to use try-with-resources. It was added to the Java language in Java 7. See https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html for a more detailed explanation of this Java language feature.
Here's what your code looks like with try-with-resources
public static void main(String[] args) throws Exception /* bad idea */ {
String sql = "SELECT id, name from user";
try (Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root");
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql))
{
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
}
}
Note that the try-with-resources will automatically close each of the declared resources (connection, statement and rs) in the reverse order that they were declared, and will deal appropriately with any exceptions that may arise during that. So we don't need any explicit close() calls.
If this was production code, we should NOT declare main as throwing exceptions. We should be dealing with the exceptions properly; i.e. logging them and/or producing a user friendly error message. I'm just taking a shortcut here ... because that's a side-bar to your original question.

You can make your code cleaner with try-with-resources, like that-
try(Connection connection = DriverManager.getConnection(
"jdbc:mysql://localhost/test",
"root",
"root"
);Statement statement = connection.createStatement()) {
String sql = "SELECT id, name from user";
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.print("ID: " + id);
System.out.print(", name: " + name);
System.out.println();
}
rs.close();
} catch (Exception se) {
se.printStackTrace();
}
It'll handle closing resources. For details go to Oracle Docs

DataSource
Set up a DataSource object with all your connection details such as username and password. Your JDBC driver likely comes with a simple implementation.
SomeDataSourceImplementation ds = new SomeDataSourceImplementation() ;
ds.setUserName( "Scott" ) ;
ds.setPassword( "Tiger" ) ;
…
DataSource dataSource = ds ;
Try-with-resources syntax
The try-with-resources syntax was added years ago to help simplify such code. See Tutorial by Oracle.
If an exception is thrown, the AutoCloseable objects are closed in the reverse order in which they were listed.
String sql = "SELECT id, name from user";
try (
Connection conn = dataSource.getConnection() ;
Statement statement = conn.createStatement() ;
ResultSet rs = statement.executeQuery(sql) ;
) {
while ( rs.next() ) {
int id = rs.getInt( "id" ) ;
String name = rs.getString( "name" ) ;
System.out.print( "ID: " + id );
System.out.print( ", name: " + name ) ;
System.out.println();
}
} catch ( SQLException e ) {
e.printStackTrace();
}

Related

SQL injection issue in java code

I have below main method which shows SQL injection flaw (as string concatenation is done here) when scanned for coding standards/rules.
public static void main(String[] args) {
boolean flag = false;
String name = "";
String subName = "abhi";
try {
Class.forName("org.postgresql.Driver");
Connection c = DriverManager.getConnection("url","user", "password");
if(flag==true){
name = "LIKE '%'";
} else {
name = "= LOWER('" + subName + "')";
}
Statement s = c.createStatement();
String query = "SELECT * FROM xyz WHERE name "+name;
ResultSet rs = s.executeQuery(query);
while(rs.next()){
System.out.println(":"+rs.getString(1));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException se) {
se.printStackTrace();
}
}
I want to remove the SQL injection flaw. As my name parameter is dynamic, I cannot set it with preparedStatement. What can be a optimal solution to this?
NOTE: Using 2 different queries in if-else block will not solve the purpose as I have 7 different parameters to be set dynamically which will introduce overhead as there will be many queries.
As my name parameter is dynamic, I cannot set it with preparedStatement.
Sure you can, you just need to treat both the SQL text and the parameters dynamically at the same time.
You should also use try-with-resources, to correctly close the Connection, PreparedStatement, and ResultSet objects.
boolean flag = false;
String subName = "abhi";
try (Connection c = DriverManager.getConnection("url","user", "password")) {
String sql = "SELECT *" +
" FROM xyz" +
" WHERE name " + (flag ? "LIKE '%'"
: "= LOWER(?)");
try (PreparedStatement s = c.prepareStatement(sql)) {
if (! flag)
s.setString(1, subName);
try (ResultSet rs = s.executeQuery()) {
while (rs.next()) {
System.out.println(":"+rs.getString(1));
}
}
}
} catch (SQLException se) {
se.printStackTrace();
}
FYI: WHERE name LIKE '%' is the same as WHERE name IS NOT NULL, which is the same as no WHERE clause if the name column is not nullable.

JDBC Returns empty result set,but records are present in database

public class alldata {
public static void main(String arg[])
{
String str;
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con=DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:xe","system","root");
PreparedStatement stmt2;
String sql="select * from RECORDDATA where ID= ? and COMPANY_NM= ?";
stmt2=con.prepareStatement(sql);
int i=5;
str="Audi";
try
{
stmt2.setInt(1, i);
System.out.println("after setting id");
stmt2.setString(2,str);
System.out.println("after setting string");
}
catch(Exception iner)
{
System.out.println("int coulnm"+iner);
}
try
{
ResultSet showdata=stmt2.executeQuery();
System.out.println("after rs");
while(showdata.next())
{
System.out.println("in next");
System.out.println("\n"+showdata.getInt(1)+showdata.getString(2)+showdata.getString(3)+showdata.getString(4)+showdata.getString(5)+showdata.getString(6)+showdata.getString(7));
System.out.println("after next");
}
}catch(Exception e)
{
e.printStackTrace();
}
}catch(Exception e2)
{
e2.printStackTrace();
}
}
}
if i uses hardcoded values ID=5 and COMPANY_NM=Audi it retrives 3 records from database... but my program accepts these value dynamically from user...so that is not working.... please help...i m stuck at this stage...
showdata.next() method does not executing.. although there are 3 records are present in database.
If you expect more than one record to be returned, why can I not find a loop in your code to iterate those records?
Also, next() will retrieve the next record. Calling it twice means you're looking at the second retrieved record.
Now for other stuff:
Java naming convention is a class name must start with uppercase letter.
You don't need Class.forName("oracle.jdbc.driver.OracleDriver"). That isn't needed for JDBC drivers compatible with Java 6 or later.
Remove all try-catch statements. You're catching errors, then letting code continue running as if nothing was wrong. Don't do that!
You're not release the resources. Use try-with-resources for better resource management. It's also simpler to use.
class Alldata {
public static void main(String arg[]) throws SQLException {
int id = 5;
String name = "Audi";
try (Connection con = DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:xe", "system", "root")) {
String sql = "select * from RECORDDATA where ID= ? and COMPANY_NM= ?";
try (PreparedStatement stmt = con.prepareStatement(sql)) {
stmt.setInt(1, id);
stmt.setString(2, name);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
System.out.printf("\n%d, %s, %s, %s, %s, %s, %s%n", rs.getInt(1),
rs.getString(2), rs.getString(3), rs.getString(4),
rs.getString(5), rs.getString(6), rs.getString(7));
}
}
}
}
}
}
Note that you're using the system schema. You should not create user tables in the system schema.
User system should only be used for database maintenance, e.g. to create new users. It should never be used by an application.
UPDATE
From comments, it would appear that statement works when hardcoded, but not when using ? markers. Just to be sure I got that right, please try this code:
class Alldata {
public static void main(String arg[]) throws SQLException {
try (Connection con = DriverManager.getConnection("jdbc:oracle:thin:#localhost:1521:xe", "system", "root")) {
String sql1 = "select count(*) from RECORDDATA where ID= ? and COMPANY_NM= ?";
try (PreparedStatement stmt = con.prepareStatement(sql1)) {
stmt.setInt(1, 5);
stmt.setString(2, "Audi");
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next())
System.out.println("Count 1: " + rs.getInt(1));
}
}
String sql2 = "select count(*) from RECORDDATA where ID= 5 and COMPANY_NM= 'Audi'";
try (PreparedStatement stmt = con.prepareStatement(sql2)) {
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next())
System.out.println("Count 2: " + rs.getInt(1));
}
}
}
}
}
It should print the same number for both "Count 1" and "Count 2".
System.out.println(showdata.next()); retrieved your first record.
Every call to .next() pushes the pointer forward. So prevent calling .next() if you are not using the retrieved result direct afterwards.

maximum open cursors exceeded exception in java and oracle

I having problem with my code:
ERROR: ORA-01000: maximum open cursor exceeded. This code is called from multiple threads.
Q. is oracle cursors are differen than JDBC cursor(resultset)?
public static void viewTable(Connection con, String TBName)
throws SQLException {
Statement stmt = null;
String query = "select *from " + TBName;
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String studentName = rs.getString("STD_NAME");
System.out.println(studentName + "\t");
}
} catch (SQLException e ) {
e.printStackTrace();
} finally {
if (stmt != null) { stmt.close(); }
}
}
Use try-with-resources to ensure that both statement and result set are closed.
String query = "select STD_NAME from " + TBName;
try (Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
String studentName = rs.getString("STD_NAME");
System.out.println(studentName + "\t");
}
} // Closes rs and stmt even with exceptions.
You should always close the ResultSet and the Statement. As you said, your code is accessed by many thread so a lot of Resultset will be open and never
closed.
Put the ResultSet variable out of the try catch block and close it in a finally, beside the Statement closing.

Java JDBC Incorrect Syntax Error

I have this method to load the objects, however when I am running the sql code it is giving me a Syntax error.
public void loadObjects() {
Statement s = setConnection();
// Add Administrators
try {
ResultSet r = s.executeQuery("SELECT * FROM Administrator;");
while (r.next()) {
Administrator getUser = new Administrator();
getUser.ID = r.getString(2);
ResultSet r2 = s.executeQuery("SELECT * FROM Userx WHERE ID= {" + getUser.ID + "};");
getUser.name = r2.getString(2);
getUser.surname = r2.getString(3);
getUser.PIN = r2.getLong(4);
JBDeveloping.users.administrators.add(getUser);
}
} catch (Exception e) {
System.out.println(e);
}
}
I have tried inserting the curly braces as stated in other questions, but I am either doing it wrong or it doesn't work.
This method should be able to load all administrators but I believe it is only inserting half of the ID.
The ID that it gets, consists of numbers and char; example "26315G"
the Error -
com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near '26315'.
Edit -
private java.sql.Connection setConnection(){
java.sql.Connection con = null;
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://" + host + ";DatabaseName=" + database + ";integratedSecurity=true;";
con = DriverManager.getConnection(url, username, password);
} catch(Exception e) {
System.out.println(e);
}
return con;
}
public void loadObjects() {
java.sql.Connection con = setConnection();
// Add Administrators
try {
PreparedStatement sql = con.prepareStatement("SELECT * FROM Administrator");
ResultSet rs = sql.executeQuery();
while (rs.next()) {
Administrator getUser = new Administrator();
getUser.ID = rs.getString(2);
PreparedStatement sql2 = con.prepareStatement("SELECT * FROM Userx WHERE ID=?");
sql2.setString(1, getUser.ID);
ResultSet r2 = sql2.executeQuery();
getUser.name = r2.getString(2);
getUser.surname = r2.getString(3);
getUser.PIN = r2.getLong(4);
JBDeveloping.users.administrators.add(getUser);
}
} catch (Exception e) {
System.out.println(e);
}
}
Actually it is not the way to do that in JDBC. That way, even if you sort your syntax error, your code is prone to sql injection attacks.
The right way would be:
// Let's say your user id is an integer
PreparedStatement stmt = connection.prepareStatement("select * from userx where id=?");
stmt.setInt(1, getUser.ID);
ResultSet rs = stmt.executeQuery();
This way you are guarded against any attempt to inject SQL in your application request parameters
First of all: if you use concurrently result-sets, you must use separate statements for each one of them (you can not share Statement s between two r and r2). And more, you lack r2.next() before reading from it.
On the other hand: it would be much more effective to use PreparedStatement in the loop that to rewrite the query all the time.
So I'd go for something like this:
public void loadObjects() {
try (
Statement st = getConnection().createStatement();
//- As you read (later) only id, then why to use '*' in this query? It only takes up resources.
ResultSet rs = st.executeQuery("SELECT id FROM Administrator");
PreparedStatement ps = getConnection().prepareStatement("SELECT * FROM Userx WHERE ID = ?");
ResultSet r2 = null;
) {
while (rs.next()) {
Administrator user = new Administrator();
user.ID = rs.getString("id");
ps.setInt(1, user.ID);
r2 = ps.executeQuery();
if (r2.next()) {
user.name = r2.getString(2);
user.surname = r2.getString(3);
user.PIN = r2.getLong(4);
JBDeveloping.users.administrators.add(user);
}
else {
System.out.println("User with ID=" + user.ID + " was not found.");
}
}
}
catch (Exception x) {
x.printStacktrace();
}
}
Please note use of Java7 auto-close feature (you didn't close resources in you code). And last note: until you are not separating statements in your queries, as to JDBC documentation, you should not place ';' at the end of statements (in all cases you shouldn't place ';' as the last character in you query string).
You should not use {} and you should not append parameters into a SQL query like this.
Remove the curly braces and use PreparedStatement instead.
see http://www.unixwiz.net/techtips/sql-injection.html

could not fetch all data from database in resultset

i am fetching all data from database and stored the resultset to a list. but could not fetch all data. i want to store the data in a dropdownlist. My code is bellow.
public static void updateChallan(){
ChallanNumber pd=null;
int i=0;
String customerName="";
List<ChallanNumber> challanList= new ArrayList<ChallanNumber>();
Connection con = DB.getConnection();
try
{
String st="select CHALLAN_NUMBER,CUSTOMER_CODE,CHALLAN_DATE from DELIVERY_CHALLAN_DETAILS order by CHALLAN_NUMBER";
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery(st);
while(rs.next())
{
String stCustName="select CUSTOMER_NAME from CUSTOMER_DETAILS where CUSTOMER_CODE='"+rs.getString(2)+"'";
Statement stmtCustName=con.createStatement();
ResultSet rsCustName=stmtCustName.executeQuery(stCustName);
while(rsCustName.next()){
customerName=rsCustName.getString(1);
}
customerName=rsCustName.getString(1);
//System.out.println(customerName +" "+i);
pd=new ChallanNumber(rs.getString(1),customerName,rs.getString(3));
challanList.add(i,pd);
i++;
}
}
catch(Exception e)
{
//e.printStackTrace();
}
render(challanList);
}
Dropdownlish code is in bellow.
<select name="challanNumber" id="challanNumber">
<option value="selected" selected="selected">ChallanNumber-CustomerCode- Date</option>
#{list challanList, as:'cl'}
<option value="${cl.challanNumber}">${cl.challanNumber}(${cl.customercode}-${cl.challanDate})</option>
#{/list}
</select>
The problem is that you are not closing the Connection and ResultSet when you get an exception. And so the database has exhausted all open cursors.
You need to close everything you open, it means statement, resultsets. You do that in the finally part of your try/catch to ensure things are correctly closed.
When you close a statement, the resultset linked to that statement is closed too.
public static void updateChallan() throws Exception {
ChallanNumber pd = null;
int i=0;
String customerName = "";
List<ChallanNumber> challanList= new ArrayList<ChallanNumber>();
Connection con = DB.getConnection();
Statement stmt = null;
try {
String st = "select CHALLAN_NUMBER,CUSTOMER_CODE,CHALLAN_DATE from DELIVERY_CHALLAN_DETAILS order by CHALLAN_NUMBER";
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(st);
while (rs.next()) {
String stCustName = "select CUSTOMER_NAME from CUSTOMER_DETAILS where CUSTOMER_CODE='" + rs.getString(2) + "'";
Statement stmtCustName = con.createStatement();
try {
ResultSet rsCustName = stmtCustName.executeQuery(stCustName);
while (rsCustName.next()){
customerName = rsCustName.getString(1);
}
} finally {
if (stmtCustName != null)
stmtCustName.close();
}
customerName = rsCustName.getString(1);
//System.out.println(customerName +" "+i);
pd = new ChallanNumber(rs.getString(1), customerName, rs.getString(3));
challanList.add(i, pd);
i++;
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if (stmt != null)
stmt.close();
}
render(challanList);
}
Besides, you should read the docs of PlayFramework (here for Play2) there are database stuff to avoid using ResultSets and Statements directly, dealing with higher structures like domain objects, the framework will do the rest for you.

Categories