Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

55
Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009 ACCU Conference April 2009 Validus 8 Thorpe Road Norwich NR1 1RY www.validus-ivc.co.uk Paul Grenyer Senior Software Engineer [email protected] http://paulgrenyer.blogspot.com

description

Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009 ACCU Conference April 2009. Paul Grenyer Senior Software Engineer [email protected] http://paulgrenyer.blogspot.com. Validus 8 Thorpe Road Norwich NR1 1RY www.validus-ivc.co.uk. - PowerPoint PPT Presentation

Transcript of Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Page 1: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Boiler Plating Database Resource CleanupWith Execute Around Method

ACCU London February 2009ACCU Conference April 2009

Validus 8 Thorpe Road

Norwich NR1 1RY

www.validus-ivc.co.uk

Paul GrenyerSenior Software [email protected]://paulgrenyer.blogspot.com

Page 2: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Agenda

All About Me

The Problem (audience participation)

Execute Around Method

A solution

Page 3: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Speaker biography

Who am I?

My career so far

Validus-IVC and what I do there

Page 4: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

The Problem

Page 5: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Audience Participation 1

Write some JDBC code to execute the following update statement:

UPDATE SERVICES SET URL="http://prodserv01/axis/services/email2" WHERE NAME = 'EMAIL'

Key concept: Connection User

Page 6: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Creating a Connectiontry{ Class.forName(DRIVER); final Connection con = DriverManager.getConnection( CONNECTION_STRING, USERNAME, PASSWORD); con.setCatalog(DATABASE);

// Use connection

}catch(ClassNotFoundException e){ // handle error}catch(SQLException e){ // handle error}

Page 7: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Creating and using a Statement ...

final Statement stmt = con.createStatement();

try

{

stmt.execute(SQL);

}

finally

{

try

{

stmt.close();

}

catch(SQLException e)

{

// handle error

}

}

...

Page 8: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Wouldn't it be nicer if you could do this?

try{ final ConnectionProvider cp = new ConnectionProvider(new StringConnection(DRIVER, CONNECTION_STRING) .setUser(USERNAME,PASSWORD) .setDatabase(DATABASE));

Query.execute( cp, "UPDATE … WHERE NAME = 'Email'");}catch(Exception e){ // Report error}

Page 9: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Audience Participation 2

Write some JDBC code to execute the following select statement:

SELECT URL FROM SERVICES

and write the result to the console

Key concept: Connection Value

Page 10: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Creating and using a Result Set...

final Statement stmt = con.createStatement();

try

{

final ResultSet rs = stmt.executeQuery(SQL);

try

{

while(rs.next())

{

System.out.println(rs.getString("url"));

}

}

finally

{

try

{

rs.close();

}

catch(Exception e)

{

// Handle exception

}

}

}

...

Page 11: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Wouldn't it be nicer if you could do this? try{ final ConnectionProvider cp = new ConnectionProvider(new StringConnection(DRIVER,CONNECTION_STRING) .setUser(USERNAME, PASSWORD) .setDatabase(DATABASE)); final List<String> urls = Query.execute( cp, "SELECT URL FROM SERVICES", new RSProcessor()); for(String url : urls) { System.out.println(url); }}catch(Exception e){ e.printStackTrace();}

Page 12: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Wouldn't it be nicer if you could do this? class RSProcessor extends AbstractResultSetFunction<List<String>>{ @Override public List<String> read(ResultSet rs) { List<String> result = new ArrayList<String>(); try { while (rs.next()) { result.add(rs.getString("url")); } } catch(SQLException ex) { getErrorPolicy().handleError(ex); } return result; }}

Page 13: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Execute Around Method (EAM)

“Encapsulate pairs of actions in the object that requires them, not code that uses the object, and pass usage code to the object as another

object.”

Kevlin Henney

?

Page 14: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

File Writer

Page 15: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

public interface WriterUser

{

void use(Writer writer) throws IOException ;

}

public class FileWriterProvider

{

private final String filename;

public FileWriterProvider(String filename)

{ this.filename = filename; }

public void provideTo(WriterUser user) throws IOException

{

final FileWriter writer = new FileWriter(filename);

try

{

user.use(writer);

}

finally

{

writer.close();

}

}

}

Page 16: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

new FileWriterProvider("out.txt").provideTo(new WriterUser()

{

@Override

public void use(Writer writer) throws IOException

{

writer.write("Execute Around Method!");

}

});

Page 17: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Execute Around Method (EAM)

“Using an object to manage a resource and provide it to another object to use”

Page 18: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

The Solution

Resource management

Providers (inc. connection factory) & Users

Error handling

Page 19: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Resource Management

Resources to manage:

Connection Statement ResultSet

Each must be:

Acquired

Used

Cleaned-up

Page 20: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider

<<Connection User>>

<<Connection Value>>

Statement Provider

<<Statement Value>>

ResultSet Provider

<<Statement User>>

<<ResultSet Function>>

Page 21: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider

The concept of a connection provider was suggested to me by Adrian Fagg. The idea is that:

A single class is responsible for acquiring a connection Providing it to another class for use Releasing it again

Execute Around Method!

Page 22: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider

Page 23: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Factory Interface

public interface ConnectionFactory{ Connection connect(); void disconnect(Connection con);}

Why have a connection factory?

On demand connection Connection pool Existing connection Custom connection

Page 24: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Factory: disconnect

public abstract class AbstractConnectionFactory implements ConnectionFactory{ @Override public void disconnect(Connection con) { if (con != null) { try { con.close(); } catch(final SQLException ex) { // To do } } } }

A Connection can be created any number of ways. A Connection is usually closed in the same way.

Page 25: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Factory: connect

public Connection connect()

{

Connection con = null;

try

{

Class.forName(driver);

con = DriverManager.getConnection( connectionString,

username,

password);

}

catch(ClassNotFoundException ex)

{

// To do

}

catch(SQLException ex)

{

// To do

}

return con;

}

Page 26: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider: Constructionpublic final class ConnectionProvider

{

private final ConnectionFactory conFactory;

public ConnectionProvider(ConnectionFactory conFactory)

{

this.conFactory = conFactory;

}

}

Page 27: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Key Concepts

User:

An object that uses a JDBC resource, but doesn’t return a value.

public interface ConnectionUser{ void use(Connection con);}

Value User:

An object that uses a JDBC resource and returns a value.

public interface ConnectionValue<T> { T fetch(Connection con);}

Page 28: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection User Interfacepublic interface ConnectionUser

{

void use(Connection con);

}

Uses the connection Does not return a value!

Page 29: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider: Userpublic void provideTo( ConnectionUser user )

{

final Connection con = conFactory.connect();

try

{

user.use(con);

}

finally

{

conFactory.disconnect(con);

}

}

Uses Finally for Each Release Pattern to:

Create a Connection from the factory Passes the Connection to the user Closes Connection

Page 30: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Value Userpublic interface ConnectionValue<T>

{

T fetch(Connection con);

}

Like the ConnectionUser, but:

Fetch method has a return type Return type is a generic parameter

Page 31: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider: Value Userpublic <T> T provideTo( ConnectionValue<T> valueUser )

{

final Connection con = conFactory.connect();

T result = null;

try

{

result = valueUser.fetch(con);

}

finally

{

conFactory.disconnect(con);

}

return result;

}

Uses Finally for Each Release pattern to:

Create a Connection from the factory Passes the Connection to the user and stores the result Closes connection

Passes the result back.

Page 32: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Statement Provider

Page 33: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Statement User and Value Userpublic interface StatementUser

{

void use(Statement stmt);

}

public interface StatementValue<T>

{

T use(Statement stmt);

}

Page 34: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Statement Provider: Value Userpublic <T> T provideTo( StatementValue<T> valueUser )

{

T result = null;

try

{

final Statement stmt = con.createStatement();

try

{

result = valueUser.use(stmt);

}

finally

{

try

{

stmt.close();

}

catch(SQLException ex)

{

// To do

}

}

}

catch(SQLException ex)

{

// To do

}

return result;

}

Page 35: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Result Set Provider

Page 36: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

ResultSetFunctionpublic interface ResultSetFunction<T>

{

T read(ResultSet rs);

}

Iterates through RecordSet. Returns result.

Page 37: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

ResultSet Provider public <T> T provideTo(ResultSetFunction<T> fetcher)

{

T result = null;

try

{

final ResultSet rs = stmt.executeQuery(sql);

try

{

result = fetcher.read(rs);

}

finally

{

try

{

rs.close();

}

catch(Exception ex)

{

// To do

}

}

}

catch(SQLException ex)

{

// To do

}

return result;

}

Page 38: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider

<<Connection User>>

<<Connection Value>>

Statement Provider

<<Statement Value>>

ResultSet Provider

<<Statement User>>

<<ResultSet Function>>

Putting It All Together.

Page 39: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Error Policy

A common error policy allows the client to decide how to handle errors:

public interface ErrorPolicy{ void handleError(Exception ex) void handleCleanupError(Exception ex)}

Clean-up exceptions could be handled differentlyMay be logged, rather than re-thrownDecision can be made by the client

Page 40: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

public <T> T provideTo( StatementValue<T> valueUser ){ T result = null; try { final Statement stmt = con.createStatement(); try { result = valueUser.use(stmt); } finally { try { stmt.close(); } catch(SQLException ex) { // handle clean-up exception } } } catch(SQLException ex) { // Handle exception } return result;}

Page 41: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

To Check or Not To Check

Checked ExceptionsInterface methods can throw Exception.Interface methods can throw a custom exception that all checked exceptions must be translated into.

public interface ErrorPolicy

{

void handleError(Exception ex) throws Exception;

void handleCleanupError(Exception ex) throws Exception;

}

public interface ErrorPolicy

{

void handleError(Exception ex) throws CustomException;

void handleCleanupError(Exception ex) throws CustomException;

}

Page 42: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

To Check or Not To Check

Runtime ExceptionsThe implementer of the interface is forced to translate checked exceptions to runtime exceptions.

public interface ErrorPolicy

{

void handleError(Exception ex)

void handleCleanupError(Exception ex)

}

Page 43: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Default Error Policypublic class DefaultErrorPolicy implements ErrorPolicy{ private Exception firstException = null; @Override public void handleCleanupError(Exception ex) { handleError(ex); }

@Override public void handleError(Exception ex) { if (firstException == null) { firstException = ex; throw new RuntimeException(ex); } }}

Page 44: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Error Policy User

public interface ErrorPolicyUser{ void setErrorPolicy(ErrorPolicy errorPolicy);}

All classes that use an ErrorPolicy must implement the ErrorPolicyUser interface.

The ErrorPolicyUser is used by providers to pass an ErrorPolicy to its Users and Values (dependency injection).

All Users and Values must therefore be an ErrorPolicyUser.

Page 45: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

public interface ConnectionFactory extends ErrorPolicyUser{ Connection connect(); void disconnect(Connection con);}

public interface ConnectionUser extends ErrorPolicyUser{ void use(Connection con);}

public interface ConnectionValue<T> extends ErrorPolicyUser{ T fetch(Connection con);}

public interface StatementUser extends ErrorPolicyUser{ void use(Statement stmt);}

public interface StatementValue<T> extends ErrorPolicyUser{ T use(Statement stmt);}

public interface ResultSetFunction<T> extends ErrorPolicyUser{ T read(ResultSet rs);}

Page 46: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

public final class StatementProvider { public void provideTo( StatementUser user ) { user.setErrorPolicy(errorPolicy); try { final Statement stmt = con.createStatement(); try { user.use(stmt); } finally { try { stmt.close(); } catch(SQLException ex) { errorPolicy.handleCleanupError(ex); } } } catch(SQLException ex) { errorPolicy.handleError(ex); } }}

Page 47: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Common Error Policy Userpublic abstract class AbstractErrorPolicyUser implements ErrorPolicyUser{ private ErrorPolicy errorPolicy = new DefaultErrorPolicy(); protected ErrorPolicy getErrorPolicy() { return errorPolicy; } @Override public void setErrorPolicy(ErrorPolicy errorPolicy) { this.errorPolicy = errorPolicy; } }

The CommonErrorPolicyUser provides a common: ErrorPolicy set method ErrorPolicy reference ErrorPolicy accessor

Page 48: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Composition

Unnecessary ErrorPolicyUser reference. Unnecessary set/get ErrorPolicy methods implemented in

ConcreteUser.

Page 49: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Inheritance

ConcreteConnection inherits set/get methods for free. ConecreteConnection IS-A ErrorPolicyUser

Page 50: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

public class Execute extends AbstractErrorPolicyUser implements StatementUser { ... @Override public void use(Statement stmt) { try { for(String s : sql) { stmt.execute(s); } } catch(SQLException ex) { getErrorPolicy().handleError(ex); } }}

Page 51: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Sorted!

Page 52: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Connection Provider

<<Connection User>>

<<Connection Value>>

Statement Provider

<<Statement Value>>

ResultSet Provider

<<Statement User>>

<<ResultSet Function>>

Page 53: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Reminder try{ final ConnectionProvider cp = new ConnectionProvider(new StringConnection(DRIVER,CONNECTION_STRING) .setUser(USERNAME, PASSWORD) .setDatabase(DATABASE)); final List<String> urls = Query.execute( cp, "SELECT URL FROM SERVICES", new RSProcessor()); for(String url : urls) { System.out.println(url); }}catch(Exception e){ e.printStackTrace();}

Page 54: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Reminderclass RSProcessor extends AbstractResultSetFunction<List<String>>{ @Override public List<String> read(ResultSet rs) { List<String> result = new ArrayList<String>(); try { while (rs.next()) { result.add(rs.getString("url")); } } catch(SQLException ex) { getErrorPolicy().handleError(ex); } return result; }}

Page 55: Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009

Anymore questions?See you in the bar…

Validus 8 Thorpe Road

Norwich NR1 1RY

www.validus-ivc.co.uk

Paul GrenyerSenior Software [email protected]://paulgrenyer.blogspot.com