Spring不管是與ibatis,仍是與Hibernate的結合中,都使用到了Template模式與callback技術,來達到簡化代碼實現的目的。Template模式也即模板模式,用於對一些不太變化的流程進行模板化,與callback結合,能夠將變化的部分出離出來,使用callback實現。而後根據不一樣的狀況,向template注入不一樣的callback。那些模板代碼就沒有必要重複寫了。咱們看下spring和ibatis的結合中,Template和callback的使用:spring
public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations { // ... ... /** * Execute the given data access action on a SqlMapExecutor. * @param action callback object that specifies the data access action * @return a result object returned by the action, or <code>null</code> * @throws DataAccessException in case of SQL Maps errors */ public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Assert.notNull(this.sqlMapClient, "No SqlMapClient specified"); // We always need to use a SqlMapSession, as we need to pass a Spring-managed // Connection (potentially transactional) in. This shouldn't be necessary if // we run against a TransactionAwareDataSourceProxy underneath, but unfortunately // we still need it to make iBATIS batch execution work properly: If iBATIS // doesn't recognize an existing transaction, it automatically executes the // batch for every single statement... SqlMapSession session = this.sqlMapClient.openSession(); if (logger.isDebugEnabled()) { logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation"); } Connection ibatisCon = null; try { Connection springCon = null; DataSource dataSource = getDataSource(); boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy); // Obtain JDBC Connection to operate on... try { ibatisCon = session.getCurrentConnection(); if (ibatisCon == null) { springCon = (transactionAware ? dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource)); session.setUserConnection(springCon); if (logger.isDebugEnabled()) { logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation"); } } else { if (logger.isDebugEnabled()) { logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation"); } } } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } // Execute given callback... try { return action.doInSqlMapClient(session); } catch (SQLException ex) { throw getExceptionTranslator().translate("SqlMapClient operation", null, ex); } finally { try { if (springCon != null) { if (transactionAware) { springCon.close(); } else { DataSourceUtils.doReleaseConnection(springCon, dataSource); } } } catch (Throwable ex) { logger.debug("Could not close JDBC Connection", ex); } } // Processing finished - potentially session still to be closed. } finally { // Only close SqlMapSession if we know we've actually opened it // at the present level. if (ibatisCon == null) { session.close(); } } }
public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException
該方法就是一個模板方法,方法的參數是一個回調對象。在模板方法中,將一些相同的處理過程模板化,好比得到數據庫鏈接,處理事務,處理異常,關閉資源等等,
這些都是每個sql執行時都要面臨的相同的過程,因此咱們將他們房子模板方法中。而後將不一樣的部分經過 callback 對象做爲參數傳入進去,這樣將模板代碼和非
模板代碼進行了隔離。沒有必要將模板代碼每次都寫一遍。
public Object queryForObject(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback<Object>() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.queryForObject(statementName, parameterObject); } }); } public List queryForList(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback<List>() { public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.queryForList(statementName, parameterObject); } }); } public Map queryForMap( final String statementName, final Object parameterObject, final String keyProperty) throws DataAccessException { return execute(new SqlMapClientCallback<Map>() { public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.queryForMap(statementName, parameterObject, keyProperty); } }); }
咱們看一下上面這些方法,都是藉助模板方法來處理那些每次都相同的流程,而後傳入一個本身實現的 callback 對象。模板會自動回調咱們在 callback 對象中定義的方法。sql
咱們看下JdbcTemplate中的模板方法也是類似的:數據庫
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); try { Connection conToUse = con; if (this.nativeJdbcExtractor != null) { // Extract native JDBC Connection, castable to OracleConnection or the like. conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } else { // Create close-suppressing Connection proxy, also preparing returned Statements. conToUse = createConnectionProxy(con); } return action.doInConnection(conToUse); } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex); } finally { DataSourceUtils.releaseConnection(con, getDataSource()); } }