Me encontré con un buen ejemplo en primavera. El marco utiliza el concepto de definiciones de clases locales dentro del método para manejar varias operaciones de base de datos de manera uniforme.
Suponga que tiene un código como este:
JdbcTemplate jdbcOperations = new JdbcTemplate(this.myDataSource);
jdbcOperations.execute("call my_stored_procedure()")
jdbcOperations.query(queryToRun, new MyCustomRowMapper(), withInputParams);
jdbcOperations.update(queryToRun, withInputParams);
Veamos primero la implementación de execute ():
@Override
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
/**
* Callback to execute the statement.
(can access method local state like sql input parameter)
*/
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
@Nullable
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
//transforms method input into a functional Object
execute(new ExecuteStatementCallback());
}
Tenga en cuenta la última línea. Spring también hace este "truco" exacto para el resto de los métodos:
//uses local class QueryStatementCallback implements StatementCallback<T>, SqlProvider
jdbcOperations.query(...)
//uses local class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider
jdbcOperations.update(...)
El "truco" con las clases locales permite que el marco se ocupe de todos esos escenarios en un solo método que acepta esas clases a través de la interfaz StatementCallback. Este método único actúa como un puente entre las acciones (ejecutar, actualizar) y las operaciones comunes a su alrededor (por ejemplo, ejecución, administración de conexiones, traducción de errores y salida de la consola dbms)
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
//
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}