Reputation: 2821
It's been a while since I have done any Java programming. And I find my self a bit stuck.
My problem is that I have a pooled db connection in tomcat. That is working nicely. But there is a lot of boiler plate required.
public void init() {
Connection conn = null;
ResultSet rst = null;
Statement stmt = null;
try {
//SETUP
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env/jdbc");
OracleDataSource ds = (OracleDataSource) envContext.lookup("tclsms");
if (envContext == null) throw new Exception("Error: No Context");
if (ds == null) throw new Exception("Error: No DataSource");
if (ds != null) conn = ds.getConnection();
if (conn == null) throw new Exception("Error: No Connection")
message = "Got Connection " + conn.toString() + ", ";
//BODY
stmt = conn.createStatement();
rst = stmt.executeQuery("SELECT 'Success obtaining connection' FROM DUAL");
if (rst.next()) message = rst.getString(1);
//TEAR DOWN
rst.close();
rst = null;
stmt.close();
stmt = null;
conn.close(); // Return to connection pool
conn = null; // Make sure we don't close it twice
} catch (Exception e) {
e.printStackTrace();
//TODO proper error handling
} finally {
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rst != null) {
try {
rst.close();
} catch (SQLException e) {;}
rst = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {;}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {;}
conn = null;
}
} //END FINALLY
} //END INIT
So I want to do the equivalent of passing a method into init that will run in the body of the function. I know I can't do this in Java. But I'm sure there must be a nice way to do this. Or at least a best practice for this sort of thing.
Any help much appreciated.
Upvotes: 2
Views: 1563
Reputation: 5648
You should prefer delegation to inheritance. The above can/will work but isn't well thought out.
Implementing Runnable on the primary class exposes it for abuse because the 'run()' method is public.
A second improvement is to use to delegate your activity to an interface (and this CAN be passed around like a function pointer whereas extending the class cannot). In addition, it makes it Spring friendly
This allows the action implementer to decide if they want multi-threaded behavior or not. You can inject composites, caching delegates, etc and the primary class is none-the-wiser. This conforms with good design practice of separation of concerns
public class MyClass {
private Action action;
public MyClass (Action action) {
this.action = action;
}
public void connection() {
try{
action.perform()
} catch (Exception e){
// handle
} finally {
tearDown();
}
}
Connection getConnection(){
return conn;
}
private void setUp(){
// SETUP here
// set the conn field
}
private void tearDown(){
// TEAR DOWN here
}
}
Upvotes: 1
Reputation: 931
abstract class UseDBConnectionTask extends Runnable {
private Connection conn;
public UseDBConnectionTask(){
setUp();
}
// should probably refine this to specific exceptions
public abstract void process() throws Exception;
public void run(){
try{
process()
// this should catch specific exceptions
} catch (Exception e){
// handle
} finally {
tearDown();
}
}
Connection getConnection(){
return conn;
}
public void setUp(){
// SETUP here
// set the conn field
}
public void tearDown(){
// TEAR DOWN here
}
}
use like:
UseDBConnectionTask dbTransaction = new UseDBConnectionTask(){
public void process(){
// do processing
// use conn via getConnection()
// eg
Statement stmt = conn.createStatement();
ResultSet rst = stmt.executeQuery("SELECT 'Success obtaining connection' FROM DUAL");
String message = null;
if (rst.next()) message = rst.getString(1);
}
}
new Thread(dbTransaction).start();
The advantage of extending Runnable is that you can then pass this instance into a thread pool or similar. Just have to be careful of threading issues. It also assumes that the tear down is always the same.
Upvotes: 1
Reputation: 931
interface IDbAction {
public DbActionResult runAction(Connection conn);
}
class DbActionResult {
Statement statement;
ResultSet resultSet;
public DbActionResult(Statement statement, ResultSet resultSet){
this.statement = statement;
this.resultSet = resultSet;
}
public void getStatement(){ return this.statement; }
public void getResultSet(){ return this.resultSet; }
}
public void runAgainstDB(IDbAction action) {
Connection conn = null;
ResultSet rst = null;
Statement stmt = null;
try {
//SETUP
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env/jdbc");
OracleDataSource ds = (OracleDataSource) envContext.lookup("tclsms");
if (envContext == null) throw new Exception("Error: No Context");
if (ds == null) throw new Exception("Error: No DataSource");
if (ds != null) conn = ds.getConnection();
if (conn == null) throw new Exception("Error: No Connection")
message = "Got Connection " + conn.toString() + ", ";
//BODY
DbActionResult actionResult = action.runAction(conn);
//TEAR DOWN
if((rst = actionResult.getResultSet()) != null){
rst.close();
rst = null;
}
if((stmt = actionResult.getStatement()) != null){
stmt.close();
stmt = null;
}
actionResult = null;
conn.close(); // Return to connection pool
conn = null; // Make sure we don't close it twice
} catch (Exception e) {
e.printStackTrace();
//TODO proper error handling
} finally {
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rst != null) {
try {
rst.close();
} catch (SQLException e) {;}
rst = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {;}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {;}
conn = null;
}
} //END FINALLY
} //END
Use like:
IDbAction action = new IDbAction(){
public DbActionResult prcoessAction(Connection conn){
Statement stmt = conn.createStatement();
ResultSet rst = stmt.executeQuery("SELECT 'Success obtaining connection' FROM DUAL");
if (rst.next()) message = rst.getString(1);
return new DbActionResult(stmt, rst);
}
}
runAgainstDB(action);
Upvotes: 0
Reputation:
private void Todo(Context initContext, Context envContext, OracleDataSource ds){
if (envContext == null) throw new Exception("Error: No Context");
if (ds == null) throw new Exception("Error: No DataSource");
if (ds != null) conn = ds.getConnection();
if (conn == null) throw new Exception("Error: No Connection")
message = "Got Connection " + conn.toString() + ", ";
//BODY
stmt = conn.createStatement();
rst = stmt.executeQuery("SELECT 'Success obtaining connection' FROM DUAL");
if (rst.next()) message = rst.getString(1);
//TEAR DOWN
rst.close();
rst = null;
stmt.close();
stmt = null;
conn.close(); // Return to connection pool
conn = null; // Make sure we don't close it twice
} catch (Exception e) {
e.printStackTrace();
//TODO proper error handling
} finally {
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rst != null) {
try {
rst.close();
} catch (SQLException e) {;}
rst = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {;}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {;}
conn = null;
}
} //END FINALLY
}
Then call it from your Init like this this. Todo(initContext,envContext , ds
)
Upvotes: 0