日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring源码解读之 JdbcTemplate源码

發布時間:2024/9/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring源码解读之 JdbcTemplate源码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文:https://blog.csdn.net/songjinbin/article/details/19857567

在Spring中,JdbcTemplate是經常被使用的類來幫助用戶程序操作數據庫,在JdbcTemplate為用戶程序提供了許多便利的數據庫操作方法,比如查詢,更新等,而且在Spring中,有許多類似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看來這是Rod.Johnson的慣用手法,

? ??所謂模板板式,就是在父類中定義算法的主要流程,而把一些個性化的步驟延遲到子類中去實現,父類始終控制著整個流程的主動權,子類只是輔助父類實現某些可定制的步驟。?

我們用代碼來說話吧:?
首先,父類要是個抽象類:?

Java代碼??

public abstract class TemplatePattern { //模板方法 public final void templateMethod(){ method1(); method2();//勾子方法 method3();//抽象方法 } private void method1(){ System.out.println("父類實現業務邏輯"); } public void method2(){ System.out.println("父類默認實現,子類可覆蓋"); } protected abstract void method3();//子類負責實現業務邏輯 }

?


父類中有三個方法,分別是method1(),method2()和method3()。?
method1()是私有方法,有且只能由父類實現邏輯,由于方法是private的,所以只能父類調用。?
method2()是所謂的勾子方法。父類提供默認實現,如果子類覺得有必要定制,則可以覆蓋父類的默認實現。?
method3()是子類必須實現的方法,即制定的步驟。?
由此可看出,算法的流程執行順序是由父類掌控的,子類只能配合。?

下面我們來寫第一個子類:?
Java代碼??

public class TemplatePatternImpl extends TemplatePattern { @Override protected void method3() { System.out.println("method3()在子類TemplatePatternImpl中實現了!!"); } }

?

這個子類只覆蓋了必須覆蓋的方法,我們來測試一下:?
Java代碼??

TemplatePattern t1 = new TemplatePatternImpl(); t1.templateMethod();

?

在控制臺中我們可以看到:?
Java代碼??
父類實現業務邏輯??
父類默認實現,子類可覆蓋??
method3()在子類TemplatePatternImpl中實現了!!??


OK,我們來看看勾子方法的使用:?
定義第2個子類,實現勾子方法:?
Java代碼??

public class TemplatePatternImpl2 extends TemplatePattern { @Override protected void method3() { System.out.println("method3()在子類TemplatePatternImpl2中實現了!!"); } /* (non-Javadoc) * @see com.jak.pattern.template.example.TemplatePattern#method2() */ @Override public void method2() { System.out.println("子類TemplatePatternImpl2覆蓋了父類的method2()方法!!"); } }

?

來測試一下:?
Java代碼??

TemplatePattern t2 = new TemplatePatternImpl2(); t2.templateMethod();

?

我們看控制臺:?
Java代碼??
父類實現業務邏輯??
子類TemplatePatternImpl2覆蓋了父類的method2()方法!!??
method3()在子類TemplatePatternImpl2中實現了!!??


OK,經典的模板模式回顧完了(大家不要拍磚哦~~~~~~~~~~)?

接下來,我們回到正題,自己模仿spring動手寫一個基于模板模式和回調的jdbcTemplate。?

回顧一下,spring為什么要封裝JDBC API,對外提供jdbcTemplate呢(不要仍雞蛋啊¥·%¥#%)?
話說SUN的JDBC API也算是經典了,曾經在某個年代折服了一批人。但隨著歷史的發展,純粹的JDBC API已經過于底層,而且不易控制,由開發人員直接接觸JDBC API,會造成不可預知的風險。還有,數據連接緩存池的發展,也不可能讓開發人員去手工獲取JDBC了。?

好了,我們來看一段曾經堪稱經典的JDBC API代碼吧:?
Java代碼??

public List<User> query() { List<User> userList = new ArrayList<User>(); String sql = "select * from User"; Connection con = null; PreparedStatement pst = null; ResultSet rs = null; try { con = HsqldbUtil.getConnection(); pst = con.prepareStatement(sql); rs = pst.executeQuery(); User user = null; while (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setBirth(rs.getDate("birth")); user.setCreateDate(rs.getDate("create_date")); userList.add(user); } } catch (SQLException e) { e.printStackTrace(); }finally{ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } try { pst.close(); } catch (SQLException e) { e.printStackTrace(); } try { if(!con.isClosed()){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } catch (SQLException e) { e.printStackTrace(); } } return userList; }

?

上面的代碼要若干年前可能是一段十分經典的,還可能被作為example被推廣。但時過境遷,倘若哪位程序員現在再在自己的程序中出現以上代碼,不是說明該公司的開發框架管理混亂,就說明這位程序員水平太“高”了。?
我們試想,一個簡單的查詢,就要做這么一大堆事情,而且還要處理異常,我們不防來梳理一下:?
1、獲取connection?
2、獲取statement?
3、獲取resultset?
4、遍歷resultset并封裝成集合?
5、依次關閉connection,statement,resultset,而且還要考慮各種異常?
6、.....?
啊~~~~ 我快要暈了,在面向對象編程的年代里,這樣的代碼簡直不能上人容忍。試想,上面我們只是做了一張表的查詢,如果我們要做第2張表,第3張表呢,又是一堆重復的代碼:?
1、獲取connection?
2、獲取statement?
3、獲取resultset?
4、遍歷resultset并封裝成集合?
5、依次關閉connection,statement,resultset,而且還要考慮各種異常?
6、.....?

這時候,使用模板模式的時機到了!!!?

通過觀察我們發現上面步驟中大多數都是重復的,可復用的,只有在遍歷ResultSet并封裝成集合的這一步驟是可定制的,因為每張表都映射不同的java bean。這部分代碼是沒有辦法復用的,只能定制。那就讓我們用一個抽象的父類把它們封裝一下吧:?
Java代碼??

public abstract class JdbcTemplate { //template method public final Object execute(String sql) throws SQLException{ Connection con = HsqldbUtil.getConnection(); Statement stmt = null; try { stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); Object result = doInStatement(rs);//abstract method return result; } catch (SQLException ex) { ex.printStackTrace(); throw ex; } finally { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if(!con.isClosed()){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } catch (SQLException e) { e.printStackTrace(); } } } //implements in subclass protected abstract Object doInStatement(ResultSet rs); }

?

在上面這個抽象類中,封裝了SUN JDBC API的主要流程,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,由子類負責實現。?
好,我們來定義一個子類,并繼承上面的父類:?
Java代碼??

public class JdbcTemplateUserImpl extends JdbcTemplate { @Override protected Object doInStatement(ResultSet rs) { List<User> userList = new ArrayList<User>(); try { User user = null; while (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setBirth(rs.getDate("birth")); user.setCreateDate(rs.getDate("create_date")); userList.add(user); } return userList; } catch (SQLException e) { e.printStackTrace(); return null; } } }

?

由代碼可見,我們在doInStatement()方法中,對ResultSet進行了遍歷,最后并返回。?
有人可能要問:我如何獲取ResultSet 并傳給doInStatement()方法啊??呵呵,問這個問題的大多是新手。因為此方法不是由子類調用的,而是由父類調用,并把ResultSet傳遞給子類的。我們來看一下測試代碼:?
Java代碼??

String sql = "select * from User"; JdbcTemplate jt = new JdbcTemplateUserImpl(); List<User> userList = (List<User>) jt.execute(sql);

?

就是這么簡單!!?

文章至此仿佛告一段落,莫急!不防讓我們更深入一些...?

試想,如果我每次用jdbcTemplate時,都要繼承一下上面的父類,是不是有些不方面呢??
那就讓我們甩掉abstract這頂帽子吧,這時,就該callback(回調)上場了?


所謂回調,就是方法參數中傳遞一個接口,父類在調用此方法時,必須調用方法中傳遞的接口的實現類。?

那我們就來把上面的代碼改造一下,改用回調實現吧:?

首先,我們來定義一個回調接口:?
Java代碼??

public interface StatementCallback { Object doInStatement(Statement stmt) throws SQLException; }

?

這時候,我們就要方法的簽名改一下了:?
Java代碼??

private final Object execute(StatementCallback action) throws SQLException

?

里面的獲取數據方式也要做如下修改:?
Java代碼??

Object result = action.doInStatement(stmt);//abstract method

?

為了看著順眼,我們來給他封裝一層吧:?
Java代碼??

public Object query(StatementCallback stmt) throws SQLException{ return execute(stmt); }

?

OK,大功告成!?
我們來寫一個測試類Test.java測試一下吧:?
這時候,訪問有兩種方式,一種是內部類的方式,一種是匿名方式。?

先來看看內部類的方式:?
Java代碼??

//內部類方式 public Object query(final String sql) throws SQLException { class QueryStatementCallback implements StatementCallback { public Object doInStatement(Statement stmt) throws SQLException { ResultSet rs = stmt.executeQuery(sql); List<User> userList = new ArrayList<User>(); User user = null; while (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setBirth(rs.getDate("birth")); user.setCreateDate(rs.getDate("create_date")); userList.add(user); } return userList; } } JdbcTemplate jt = new JdbcTemplate(); return jt.query(new QueryStatementCallback()); }

?

在調用jdbcTemplate.query()方法時,傳一個StatementCallBack()的實例過去,也就是我們的內部類。?

再來看看匿名方式:?
Java代碼??

//匿名類方式 public Object query2(final String sql) throws Exception{ JdbcTemplate jt = new JdbcTemplate(); return jt.query(new StatementCallback() { public Object doInStatement(Statement stmt) throws SQLException { ResultSet rs = stmt.executeQuery(sql); List<User> userList = new ArrayList<User>(); User user = null; while (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setBirth(rs.getDate("birth")); user.setCreateDate(rs.getDate("create_date")); userList.add(user); } return userList; } }); }

?

相比之下,這種方法更為簡潔。?
為什么spring不用傳統的模板方法,而加之以Callback進行配合呢??
試想,如果父類中有10個抽象方法,而繼承它的所有子類則要將這10個抽象方法全部實現,子類顯得非常臃腫。而有時候某個子類只需要定制父類中的某一個方法該怎么辦呢?這個時候就要用到Callback回調了。?

? ? 最后的源碼為:

package com.jak.pattern.template.callbacktemplate; import java.sql.SQLException; import java.sql.Statement;public interface StatementCallback {Object doInStatement(Statement stmt) throws SQLException; } public class JdbcTemplate {//template methodprivate final Object execute(StatementCallback action) throws SQLException{ Connection con = HsqldbUtil.getConnection();Statement stmt = null;try {stmt = con.createStatement();Object result = action.doInStatement(stmt);//abstract method return result;}catch (SQLException ex) {ex.printStackTrace();throw ex;}finally {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}try {if(!con.isClosed()){try {con.close();} catch (SQLException e) {e.printStackTrace();}}} catch (SQLException e) {e.printStackTrace();}} }public Object query(StatementCallback stmt) throws SQLException{return execute(stmt);} }

?

//調用測試類

public class Test {//內部類方式 public Object query(final String sql) throws SQLException { class QueryStatementCallback implements StatementCallback {public Object doInStatement(Statement stmt) throws SQLException {ResultSet rs = stmt.executeQuery(sql);List<User> userList = new ArrayList<User>();User user = null;while (rs.next()) {user = new User();user.setId(rs.getInt("id"));user.setUserName(rs.getString("user_name"));user.setBirth(rs.getDate("birth"));user.setCreateDate(rs.getDate("create_date"));userList.add(user);}return userList;}}JdbcTemplate jt = new JdbcTemplate();return jt.query(new QueryStatementCallback()); }//匿名類方式 public Object query2(final String sql) throws Exception{JdbcTemplate jt = new JdbcTemplate();return jt.query(new StatementCallback() {public Object doInStatement(Statement stmt) throws SQLException {ResultSet rs = stmt.executeQuery(sql);List<User> userList = new ArrayList<User>();User user = null;while (rs.next()) {user = new User();user.setId(rs.getInt("id"));user.setUserName(rs.getString("user_name"));user.setBirth(rs.getDate("birth"));user.setCreateDate(rs.getDate("create_date"));userList.add(user);}return userList;} });} public static void main(String[] args) throws Exception {String sql = "select * from User";Test t = new Test();List<User> userList = (List<User>) t.query(sql);List<User> userList2 = (List<User>) t.query2(sql);System.out.println(userList);System.out.println(userList2);} }

?


言歸正傳,有了上面的基礎后,我們正式開始閱讀源碼:

下面幾個接口是對變化的部分進行建模


接口:創建PreparedStatement。根據Connection來創建PreparedStatement。
?

public interface PreparedStatementCreator { PreparedStatement createPreparedStatement (Connection conn) throws SQLException; } 使用方法就是:PreparedStatementCreator psc = new PreparedStatementCreator() { public PreparedStatement createPreparedStatement (Connection conn) throws SQLException { PreparedStatement ps = conn. prepareStatement ( "SELECT seat_id AS id FROM available_seats WHERE " + "performance_id = ? AND price_band_id = ?"); ps.setInt(1, performanceId); ps.setInt(2, seatType); return ps; } };

?

給PreparedStatement設置參數。是對PreparedStatmentCreator的設置ps值的一個補充。

public interface PreparedStatementSetter { void setValues(PreparedStatement ps) throws SQLException; }

? ? ??

對ResultSet進行處理。還有具體的子類。
?

public interface RowCallbackHandler { void processRow(ResultSet rs) throws SQLException; }

??? ?? 使用方式:

RowCallbackHandler rch = new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { int seatId = rs.getInt(1) ; list.add(new Integer (seatId) );//典型的inner class的應用,list為外部類的變量。 } };

和上面的RowCallbackHandler類似。
?

public interface ResultSetExtractor { Object extractData(ResultSet rs) throws SQLException, DataAccessException; }

? ? ? 下面是JdbcTemplate中提供的模板方法。該方法完成對數據庫的查詢:)。

??? ?? 這個execute()方法非常關鍵。
?

public Object query( PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse) throws DataAccessException { Assert.notNull(rse, "ResultSetExtractor must not be null"); if (logger.isDebugEnabled()) { String sql = getSql(psc); //取得不變的SQL部分。 logger.debug("Executing SQL query" + (sql != null ? " [" + sql + "]" : "")); } return execute(psc, new PreparedStatementCallback() { public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { ResultSet rs = null; try { if (pss != null) { pss.setValues(ps);//就是給ps來設置參數用的。ps.setInt(1, 0); } rs = ps.executeQuery();//執行查詢 ResultSet rsToUse = rs; if (nativeJdbcExtractor != null) { rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); } return rse.extractData(rsToUse); // ResultSetExtractor從ResultSet中將值取出List。 } finally { //最后的善后工作還是需要做好的:) rs.close(),把ps的相關參數清除掉。 JdbcUtils.closeResultSet(rs); if (pss instanceof ParameterDisposer) { ((ParameterDisposer) pss).cleanupParameters(); } } } }); }

?

看看execute()方法吧。
java 代碼
?

public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action) throws DataAccessException { Assert.notNull(psc, "PreparedStatementCreator must not be null"); Assert.notNull(action, "Callback object must not be null"); //取得數據庫的連接 Connection con = DataSourceUtils.getConnection(getDataSource()); PreparedStatement ps = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } //創建PreparedStatement ps = psc.createPreparedStatement(conToUse); applyStatementSettings(ps);//這個方法是設置ps的一些屬性,我平時不用,Spring框架倒是考慮得相當全的說。 PreparedStatement psToUse = ps; if (this.nativeJdbcExtractor != null) { psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps); } //調用Callback來完成PreparedStatement的設值。就是調用上面的doInPreparedStatement來使用ps。 Object result = action.doInPreparedStatement(psToUse); SQLWarning warning = ps.getWarnings(); throwExceptionOnWarningIfNotIgnoringWarnings(warning); return result; } //如果有錯誤的話,那么就開始ps.close(), connection.close(); catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } String sql = getSql(psc); psc = null; JdbcUtils.closeStatement(ps); //就是ps.close();ps = null; DataSourceUtils.releaseConnection(con, getDataSource()); / con = null; throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex); }

? ? ? ? //不管怎么樣,ps.close(),?Connection.close()吧,當然這里是releaseConnection。在我的程序中,Connection只有一個,沒有ConnectionPool,當然不會去close?Connection。一般來講,如果沒有Connection的線程池的話,我們肯定也不會經常的關閉Connection,得到Connection。畢竟這個東西非常耗費資源。??

? ? ??

finally { if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } JdbcUtils.closeStatement(ps); DataSourceUtils.releaseConnection(con, getDataSource()); } }

?

JdbcTemplate完成了負責的操作,客戶只需要調用query()就可以完成查詢操作了。當然,JdbcTemplate會實現很多帶其它參數的方法,以方便你的使用。Template設計模式被發揚廣大了。
DataSourceUtils:這個專門用于管理數據庫Connection的類。

?

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); ~~~~~~ //這個方法很舒服,Spring Framework中到處有這樣的方法。為什么要委派到這個動作方法? } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } }

? ? 這里的doGetConnection就稍微復雜一點了。但是如果沒有事務同步管理器的話,那就比較簡單。

只是在Connection上多了一個ConnecionHolder類用于持有Connection,實現ConnectionPool的一點小功能。
?

public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); ~~~~~//Connection的持有器。通過持有器得到Connection。if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); ……return con; } ConnectionHolder:Connection的持有器。通過ConnectionHandler來完成對Connection的操作:) 典型的委派。public class ConnectionHolder extends ResourceHolderSupport { private Connection currentConnection; //當前的Connection private ConnectionHandle connectionHandle; //Connection的處理器,因此可以通過該類完成對connection的管理。 public ConnectionHolder(Connection connection) { this.connectionHandle = new SimpleConnectionHandle(connection); } public ConnectionHolder(ConnectionHandle connectionHandle) { Assert.notNull(connectionHandle, "ConnectionHandle must not be null"); this.connectionHandle = connectionHandle; } public Connection getConnection() { Assert.notNull(this.connectionHandle, "Active Connection is required"); if (this.currentConnection == null) { this.currentConnection = this.connectionHandle.getConnection(); } return this.currentConnection; } public void released() { super.released(); if (this.currentConnection != null) { this.connectionHandle.releaseConnection(this.currentConnection); this.currentConnection = null; } } connectionHandle 的接口太純粹了。但是我覺得這個設計太過于細致了:) public interface ConnectionHandle { /** * Fetch the JDBC Connection that this handle refers to. */ Connection getConnection(); /** * Release the JDBC Connection that this handle refers to. * @param con the JDBC Connection to release */ void releaseConnection(Connection con); }

?

? 最后看一下SimpleConnectionHandle,這個ConnectionHandle的簡單實現類。就只有一個Connection可管理。如果有多個Connection可管理的話,這里就是ConnectionPool了:)

java 代碼
?

public class SimpleConnectionHandle implements ConnectionHandle { private final Connection connection; /** * Create a new SimpleConnectionHandle for the given Connection. * @param connection the JDBC Connection */ public SimpleConnectionHandle(Connection connection) { Assert.notNull(connection, "Connection must not be null"); this.connection = connection; } /** * Return the specified Connection as-is. */ public Connection getConnection() { return connection; } /** * This implementation is empty, as we're using a standard * Connection handle that does not have to be released. */ public void releaseConnection(Connection con) { } public String toString() { return "SimpleConnectionHandle: " + this.connection; } }

?

總結

以上是生活随笔為你收集整理的spring源码解读之 JdbcTemplate源码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。