What's the right way to create a PreparedStatement, reuse it a few times, then clean it up? I'm using the following pattern:
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection(...);
// first use
stmt = conn.prepareStatement("some statement ?");
stmt.setString(1, "maybe some param");
if (stmt.execute()) {
...
}
// second use
stmt = conn.prepareStatement("some statement ?");
stmt.setString(1, "maybe some param");
if (stmt.execute()) {
...
}
// third use.
stmt = conn.prepareStatement("some statement");
stmt.execute();
}
finally {
if (stmt != null) {
try {
stmt.close();
} catch (Exception sqlex) {
sqlex.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception sqlex) {
sqlex.printStackTrace();
}
conn = null;
}
}
Can we reuse the "stmt" object like that, or do we have to call stmt.close() between each query?
Thanks
---------- Update ------------------------
Ah ok I see, each of my statements will be different. So is this a more correct pattern?:
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection(...);
// first use
PreparedStatement stmt1 = null;
try {
stmt1 = conn.prepareStatement("some statement ?");
stmt1.setString(1, "maybe some param");
if (stmt1.execute()) {
...
}
}
finally {
if (stmt1 != null) {
try {
stmt1.close();
} catch (Exception ex) {}
}
}
// second use
PreparedStatement stmt2 = null;
try {
stmt2 = conn.prepareStatement("some different statement ?");
stmt2.setString(1, "maybe some param");
if (stmt2.execute()) {
...
}
}
finally {
if (stmt2 != null) {
try {
stmt2.close();
} catch (Exception ex) {}
}
}
// third use
PreparedStatement stmt3 = null;
try {
stmt3 = conn.prepareStatement("yet another statement ?");
stmt3.setString(1, "maybe some param");
if (stmt3.execute()) {
...
}
}
finally {
if (stmt3 != null) {
try {
stmt3.close();
} catch (Exception ex) {}
}
}
}
finally {
if (conn != null) {
try {
conn.close();
} catch (Exception sqlex) {
sqlex.printStackTrace();
}
conn = null;
}
}
So each different statement will be closed individually before the next one executes.
It's the other way around -- you only need to prepare it once, and then reuse it.
I.E. This:
// second use
stmt = conn.prepareStatement("some statement ?");
stmt.setString(1, "maybe some param");
if (stmt.execute()) {
...
}
should become this:
// second use
stmt.setString(1, "maybe some param");
if (stmt.execute()) {
...
}
Your third use, which is a different statement, should either be a new variable, or close your prepared statement first. (Though usually with PreparedStatements, you keep them around and reuse them).
Related
How to Close Statements and Connection in This Method
public static ResultSet getData (String query){
try {
Connection con = ConnectionProvider.connect();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query);
return rs;
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e);
System.out.println(e);
return null;
}
You need to close connections in finally block:
try {
...
}
catch {
...
}
finally {
try { st.close(); } catch (Exception e) { /* Ignored */ }
try { con.close(); } catch (Exception e) { /* Ignored */ }
}
In Java 7 and higher you can define all your connections and statements as a part of try block:
try(Connection con = ConnectionProvider.connect();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query);
) {
// Statements
}
catch(....){}
One should use try-with-resources to automatically close all.
Then there is the p
public static void processData (String query, Consumer<ResultSet> processor){
try (Connection con = ConnectionProvider.connect();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query)) {
processor.accept(rs);
} catch (SQLException e) {
JOptionPane.showMessageDialog(null, e);
System.getLogger(getClass().getName()).log(Level.Error, e);
}
}
processData("SELECT * FROM USERS", rs -> System.out.println(rs.getString("NAME")));
Or
public static <T> List<T> getData (String query, UnaryOperator<ResultSet, T> convert){
try (Connection con = ConnectionProvider.connect();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query)) {
List<T> result = new ArrayList<>();
while (rs.next()) {
result.add(convert.apply(rs));
}
return result;
} catch (SQLException e) {
System.getLogger(getClass().getName()).log(Level.Error, e);
throw new IllegalArgumentException("Error in " + query, e);
}
}
Then there is the danger with this function, that users will compose query strings like:
String query = "SELECT * FROM USERS WHERE NAME = '" + name + "'";
Which does not escape the apostrophes like in d'Alembert. It opens the gates to SQL injection, a large security breach. One needs a PreparedStatement, and then can use type-safe parameters.
As with try-with-resources the code already is reduced (no explicit closes), you should drop this kind of function. But almost most programmers make this mistake.
when i wrote function instead of procedure, it compiled.
CREATE OR REPLACE function ilce_gtr
(
p_ilkodu number
)
RETURN VARCHAR2 AS
p_geridonen varchar2(1000);
begin
for rec in(SELECT ADI FROM ILCE WHERE Y_IL=p_ilkodu)
loop
p_geridonen := p_geridonen || '|' || rec.ADI;
end loop;
return p_geridonen;
end;
/
then i created xml via web method, it was successful.
#WebMethod
public String get_ilce (int p_ilkodu) {
Statement stmt=null;
ResultSet rs=null;
Connection conn=null;
String deger=null;
try {
conn= getConnection_test();
String query = "SELECT ILCE_GTR('" + p_ilkodu + "') FROM DUAL";
stmt = conn.createStatement();
rs = stmt.executeQuery(query);
while (rs.next()) {
deger = rs.getString(1);
}
} catch (Exception e) {
return "hata";
} finally {
try {
rs.close();
stmt.close();
conn.close();
} catch (SQLException e) {
return "hata";
}
}
return deger;
}
I want to do the same for inserting to database, can u help me?
#WebMethod
public String add_ilce (int yourInput) {
Statement stmt=null;
ResultSet rs=null;
Connection conn=null;
String deger=null;
try {
conn= getConnection_test();
String query = "INSERT INTO DUAL" + "(yourAttributeName)" +"VALUES (?)";
PreparedStatement preparedStmt = conn.prepareStatement(query);
preparedStmt.setString (1, yourInput);
preparedStmt.execute();
} catch (Exception e) {
return "hata";
} finally {
try {
rs.close();
stmt.close();
conn.close();
} catch (SQLException e) {
return "hata";
}
}
return deger;
}
EDIT: I suggest you to use DAO approach for such scenarios, check here: Data access object (DAO) in Java
EDIT: I edited the post now it must work as it should be, sorry I had some mistakes
web service didnt appear on localhost, there are others.
#WebMethod
public String add_ilce (String p_no, int p_tplm) {
Statement stmt=null;
ResultSet rs=null;
Connection conn=null;
String deger=null;
try {
conn= getConnection_test();
String query = "INSERT INTO DUAL" + "TEMP_TAHAKKUK_AG(ABONENO,TOPLAM)" +"VALUES ('p_no','p_tplm')";
stmt = conn.createStatement();
rs = stmt.executeQuery(query);
while (rs.next()) {
deger = rs.getString(1);
}
} catch (Exception e) {
return "hata";
} finally {
try {
rs.close();
stmt.close();
conn.close();
} catch (SQLException e) {
return "hata";
}
}
return deger;
}
I need help creating a boolean check for a method I made. I basically want it to return true if they have the specified EnumRank, but it's not working. Any suggestions?
EDIT: Both values are stored in VARCHARS
Code:
public boolean hasRank(Player player, EnumRanks rank){
if (!MySql.checkConnection()){
return false;
}
try{
String query = "SELECT RANK FROM `user_ranks` WHERE UUID= '" + player.getUniqueId() + "';";
PreparedStatement statement = MySql.getConnection().prepareStatement(query);
ResultSet result = statement.executeQuery();
result.next();
return result.getBoolean(rank.getSQLName());
//return true - So I can use this method
}
catch (SQLException e){
//Nothing
}
return false;
}
Add the rank to the query, and you're using PreparedStatement so use bind parameters. You can use result.next() to check if the query returns a result and you should use the finally block to close your Statement and ResultSet (and Connection). And don't silently swallow Exception(s). Something like
public boolean hasRank(Player player, EnumRanks rank) {
if (!MySql.checkConnection()) {
return false;
}
Connection conn = null;
PreparedStatement statement = null;
ResultSet result = null;
String query = "SELECT RANK FROM `user_ranks` WHERE UUID=? AND RANK=?";
try {
conn = MySql.getConnection();
statement = conn.prepareStatement(query);
statement.setString(1, player.getUniqueId());
statement.setString(2, rank.getSQLName());
result = statement.executeQuery();
return result.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (result != null) {
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}
Statement Leakage in JDBC:
pstmt =
StatementLeakage : An open JDBC Statement is not closed on all paths. This can cause a transaction or Statement resources to remain active indefinitely, slowing or preventing access to the database by other requests.: for (object created at line = TunnelDBHandler:139, type = java.sql.PreparedStatement), object used at prepareStatement() # TunnelDBHandler:139
dbManager
.getConnection()
.prepareStatement(
"update TUNNEL_STORE set IS_TUNNEL_OPEN=? where TUNNEL_ID=?");
Add a finally block and call close() on all of your ResultSet(s), Statement(s) and Connection(s). As a very rough example,
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// ...
// get a statement for stmt
// get a resultset from the stmt
// ...
while (rs.next()) {
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
}
There are many steps involved in executing one SQL statement in Java:
Create connection
Create statement
Execute statement, create resultset
Close resultset
Close statement
Close connection
At each of these steps SQLException can be thrown. If we to handle all exception and release all the resources correctly, the code will will look like this with 4 levels of TRY stacked on the top of each other.
try {
Connection connection = dataSource.getConnection();
try {
PreparedStatement statement = connection.prepareStatement("SELECT 1 FROM myTable");
try {
ResultSet result = statement.executeQuery();
try {
if (result.next()) {
Integer theOne = result.getInt(1);
}
}
finally {
result.close();
}
}
finally {
statement.close();
}
}
finally {
connection.close();
}
}
catch (SQLException e) {
// Handle exception
}
Can you propose a better (shorter) way to execute a statement while still release all the consumed resources?
If you are using Java 7, the try with resources statement will shorten this quite a bit, and make it more maintainable:
try (Connection conn = ds.getConnection(); PreparedStatement ps = conn.prepareStatement(queryString); ResultSet rs = ps.execute()) {
} catch (SQLException e) {
//Log the error somehow
}
Note that closing the connection closes all associated Statements and ResultSets.
Check out Apache Commons DbUtils, and in particular the closeQuietly() method. It will handle the connection/statement/result set closing correctly, including the cases where one or more are null.
An alternative is Spring JdbcTemplate, which abstracts a lot of work away from you, and you handle your database queries in a much more functional fashion. You simply provide a class as a callback to be called on for every row of a ResultSet. It'll handle iteration, exception handling and the correct closing of resources.
I create a utility class with static methods I can call:
package persistence;
// add imports.
public final class DatabaseUtils {
// similar for the others Connection and Statement
public static void close(ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
LOGGER.error("Failed to close ResultSet", e);
}
}
}
So your code would be:
Integer theOne = null;
Connection connection = null;
PreparedStatement statment = null;
ResultSet result = null;
try {
connection = dataSource.getConnection();
statement = connection.prepareStatement("SELECT 1 FROM myTable");
result = statement.executeQuery();
while (result.next()) {
theOne = result.getInt(1);
}
} catch (SQLException e) {
// do something
} finally {
DatabaseUtils.close(result);
DatabaseUtils.close(statement);
DatabaseUtils.close(connection);
}
return theOne;
I'd recommend instantiating the Connection outside this method and passing it in. You can handle transactions better that way.
Connection connection = null;
PreparedStatement statement = null;
ResultSet result = null;
try {
connection = dataSource.getConnection();
statement = connection.prepareStatement("SELECT 1 FROM myTable");
result = statement.executeQuery();
if (result.next()) {
Integer theOne = result.getInt(1);
}
}
catch (SQLException e) { /* log error */ }
finally {
if (result != null) try { result.close(); } catch (Exception e) {/*log error or ignore*/}
if (statement != null) try { statement.close(); } catch (Exception e) {/*log error or ignore*/}
if (connection != null) try { connection.close(); } catch (Exception e) {/*log error or ignore*/}
}
Just close the Connection, this releases all resources*. You don't need to close Statement and ResultSet.
*just make sure you don't have any active transactions.
Your code can be shortened and written in this way...
Connection connection = dataSource.getConnection();
PreparedStatement statement = null;
ResultSet result = null;
try {
statement= connection.prepareStatement("SELECT 1 FROM myTable");
result = statement.executeQuery();
if (result.next()) {
Integer theOne = result.getInt(1);
}
} catch (SQLException e) {
// Handle exception
} finally {
if(result != null) result.close();
if(statement != null) statement.close();
if(connection != null) connection.close();
}