1 / 55

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

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

york
Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Boiler Plating Database Resource Cleanup With Execute Around Method ACCU London February 2009 ACCU Conference April 2009 Paul GrenyerSenior Software Engineer p.grenyer@validus-ivc.co.uk http://paulgrenyer.blogspot.com Validus 8 Thorpe Road Norwich NR1 1RY www.validus-ivc.co.uk

  2. Agenda All About Me The Problem (audience participation) Execute Around Method A solution

  3. Speaker biography Who am I? My career so far Validus-IVC and what I do there

  4. The Problem

  5. 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

  6. Creating a Connection try { 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 }

  7. Creating and using a Statement ... final Statement stmt = con.createStatement(); try { stmt.execute(SQL); } finally { try { stmt.close(); } catch(SQLException e) { // handle error } } ...

  8. 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 }

  9. 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

  10. 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 } } } ...

  11. 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(); }

  12. 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; } }

  13. 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 ?

  14. File Writer

  15. 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(); } } }

  16. new FileWriterProvider("out.txt").provideTo(new WriterUser() { @Override public void use(Writer writer) throws IOException { writer.write("Execute Around Method!"); } });

  17. Execute Around Method (EAM) “Using an object to manage a resource and provide it to another object to use”

  18. The Solution Resource management Providers (inc. connection factory) & Users Error handling

  19. Resource Management Resources to manage: Connection Statement ResultSet Each must be: Acquired Used Cleaned-up

  20. Connection Provider <<Connection User>> <<Connection Value>> Statement Provider <<Statement User>> <<Statement Value>> ResultSet Provider <<ResultSet Function>>

  21. 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!

  22. Connection Provider

  23. 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

  24. 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.

  25. 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; }

  26. Connection Provider: Construction public final class ConnectionProvider { private final ConnectionFactory conFactory; public ConnectionProvider(ConnectionFactory conFactory) { this.conFactory = conFactory; } }

  27. 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); }

  28. Connection User Interface public interface ConnectionUser { void use(Connection con); } Uses the connection Does not return a value!

  29. Connection Provider: User public 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

  30. Connection Value User public interface ConnectionValue<T> { T fetch(Connection con); } Like the ConnectionUser, but: Fetch method has a return type Return type is a generic parameter

  31. Connection Provider: Value User public <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.

  32. Statement Provider

  33. Statement User and Value User public interface StatementUser { void use(Statement stmt); } public interface StatementValue<T> { T use(Statement stmt); }

  34. Statement Provider: Value User 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) { // To do } } } catch(SQLException ex) { // To do } return result; }

  35. Result Set Provider

  36. ResultSetFunction public interface ResultSetFunction<T> { T read(ResultSet rs); } Iterates through RecordSet. Returns result.

  37. 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; }

  38. Putting It All Together. Connection Provider <<Connection User>> <<Connection Value>> Statement Provider <<Statement User>> <<Statement Value>> ResultSet Provider <<ResultSet Function>>

  39. 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 differently • May be logged, rather than re-thrown • Decision can be made by the client

  40. 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; }

  41. To Check or Not To Check Checked Exceptions Interface 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; }

  42. To Check or Not To Check Runtime Exceptions The implementer of the interface is forced to translate checked exceptions to runtime exceptions. public interface ErrorPolicy { void handleError(Exception ex) void handleCleanupError(Exception ex) }

  43. Default Error Policy public 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); } } }

  44. 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.

  45. 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 StatementUserextends 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); }

  46. 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); } } }

  47. Common Error Policy User • public 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

  48. Composition • Unnecessary ErrorPolicyUser reference. • Unnecessary set/get ErrorPolicy methods implemented in ConcreteUser.

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

  50. 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); } } }

More Related