Spring源碼解析-JdbcTemplate

JdbcTemplate類圖

 

從類繼承關係上來看,JdbcTemplate繼承了基類JdbcAccessor和接口類JdbcOperation,在基類JdbcAccessor的設計中,對DataSource數據源的管理和配置,在JdbcOperation接口中,定義了經過JDBC操做數據庫的基本操做方法,而JdbcTemplate提供了這些接口的實現,例如 execute(), query() , update()等方法。java

回顧JDBC的簡單使用

@Test
public void testJDBC(){
  String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8";
  String username = "root";
  String password = "123456";
  //1.加載驅動程序
  Class.forName("com.mysql.jdbc.Driver");
  //2.獲取鏈接
  Connection conn = DriverManager.getConnection(url, username, password);
  Statement stmt = conn.createStatement();
  //3.執行SQL,獲取結果
  ResultSet  rs = stmt.executeQuery("select * from user");
  while(rs.next()){
    System.out.println("username="+rs.getStriing("username")+" age="+rs.getInt("age"));
  }
  //關閉資源
  rs.close();
  stmt.close();
  conn.close();
}

JdbcTemplate源碼分析

    JdbcTemplate的execute實現

 

execute有不少重載方法,看void execute(final String sql)方法mysql

//執行SQL語句
@Override
	public void execute(final String sql) throws DataAccessException {
		if (logger.isDebugEnabled()) {
			logger.debug("Executing SQL statement [" + sql + "]");
		}
        //回調類
		class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
			@Override
			public Object doInStatement(Statement stmt) throws SQLException {
				stmt.execute(sql);
				return null;
			}
			@Override
			public String getSql() {
				return sql;
			}
		}
		execute(new ExecuteStatementCallback());
	}

  

  //靜態處理SQL
    @Override
	public <T> T execute(StatementCallback<T> action) throws DataAccessException {
		Assert.notNull(action, "Callback object must not be null");
		//獲取數據庫鏈接
		Connection con = DataSourceUtils.getConnection(getDataSource());
		Statement stmt = null;
		try {
			Connection conToUse = con;
			if (this.nativeJdbcExtractor != null &&
					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
			}
            //建立Statement
			stmt = conToUse.createStatement();
			applyStatementSettings(stmt);
			Statement stmtToUse = stmt;
			if (this.nativeJdbcExtractor != null) {
				stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
			}
            //回調函數
			T result = action.doInStatement(stmtToUse);
			handleWarnings(stmt);
			return result;
		}
		catch (SQLException ex) {
            //釋放數據庫鏈接,同時拋出一個Spring轉換過的Spring數據庫異常
			// Release Connection early, to avoid potential connection pool deadlock
			// in the case when the exception translator hasn't been initialized yet.
			JdbcUtils.closeStatement(stmt);
			stmt = null;
			DataSourceUtils.releaseConnection(con, getDataSource());
			con = null;
			throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
		}
		finally {
            //釋放鏈接
			JdbcUtils.closeStatement(stmt);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}

execute的流程圖以下:spring

JdbcTemplate的query實現

query也有不少重載方法,void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch)sql

@Override
	public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {
		query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));
	}
@Override
	public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
      //建立Statement
		return query(new SimplePreparedStatementCreator(sql), pss, rse);
	}
public <T> T query(
			PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
			throws DataAccessException {

		Assert.notNull(rse, "ResultSetExtractor must not be null");
		logger.debug("Executing prepared SQL query");

		return execute(psc, new PreparedStatementCallback<T>() {
			@Override
			public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
				ResultSet rs = null;
				try {
					if (pss != null) {
						pss.setValues(ps);
					}
                    //執行查詢SQL
					rs = ps.executeQuery();
					ResultSet rsToUse = rs;
					if (nativeJdbcExtractor != null) {
						rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
					}
                    //抽取結果集數據
					return rse.extractData(rsToUse);
				}
				finally {
                    //關閉結果集
					JdbcUtils.closeResultSet(rs);
					if (pss instanceof ParameterDisposer) {
						((ParameterDisposer) pss).cleanupParameters();
					}
				}
			}
		});
	}

主要來看一下RowCallbackHandlerResultSetExtractor類數據庫

private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {

		private final RowCallbackHandler rch;

		public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
			this.rch = rch;
		}

		@Override
		public Object extractData(ResultSet rs) throws SQLException {
          //循環獲取每一行數據
			while (rs.next()) {
				this.rch.processRow(rs);
			}
			return null;
		}
	}

經過processRow獲取每行中的每一列的數據安全

@Override
	public final void processRow(ResultSet rs) throws SQLException {
		if (this.rowCount == 0) {
			ResultSetMetaData rsmd = rs.getMetaData();
			this.columnCount = rsmd.getColumnCount();
			this.columnTypes = new int[this.columnCount];
			this.columnNames = new String[this.columnCount];
			for (int i = 0; i < this.columnCount; i++) {
				this.columnTypes[i] = rsmd.getColumnType(i + 1);
				this.columnNames[i] = JdbcUtils.lookupColumnName(rsmd, i + 1);
			}
			// could also get column names
		}
      //由子類來實現
		processRow(rs, this.rowCount++);
	}

  流程圖以下:app

DataSourceUtils對數據庫鏈接的管理

獲取鏈接ide

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}
	}

doGetConnection方法函數

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");
		/*把數據庫的Connection放到事務管理中進行管理,這裏採用是TransactionSynchronizationManager
		中的ThreadLocal變量和線程綁定數據庫鏈接
		 */
		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		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.
		//若是從holder中沒有獲取鏈接,那就從數據源中獲取鏈接
		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = dataSource.getConnection();

		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			logger.debug("Registering transaction synchronization for JDBC Connection");
			// Use same Connection for further JDBC actions within the transaction.
			// Thread-bound object will get removed by synchronization at transaction completion.
			ConnectionHolder holderToUse = conHolder;
			if (holderToUse == null) {
				holderToUse = new ConnectionHolder(con);
			}
			else {
				holderToUse.setConnection(con);
			}
			holderToUse.requested();
            //註冊到事務管理器中
			TransactionSynchronizationManager.registerSynchronization(
					new ConnectionSynchronization(holderToUse, dataSource));
			holderToUse.setSynchronizedWithTransaction(true);
			if (holderToUse != conHolder) {
				TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
			}
		}
		return con;
	}

繼續來看一下TransactionSynchronizationManager是如何返回ConnectionHolder,最終的方法是在源碼分析

private static Object doGetResource(Object actualKey) {
        Map<Object, Object> map = (Map)resources.get();
        if(map == null) {
            return null;
        } else {
            Object value = map.get(actualKey);
            if(value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
                map.remove(actualKey);
                if(map.isEmpty()) {
                    resources.remove();
                }

                value = null;
            }

            return value;
        }
    }

是從resource中獲取一個Map,這個Map中的key就是dataSource,value爲ConnectionHolder,而Connection就是包裝在裏面,resource又是什麼類型的?

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");

      從上面能夠了解到事務是和Connection綁定在一塊兒的,同時使用了LocalThread來保存,使得線程安全。

最後問dataSource(數據源)又是從哪裏來的?dataSource做爲JdbcTemplate的基類JdbcAccessor的屬性是經過Ioc容器注入,能夠看一下項目數據源都須要在spring.xml或配置類中進行配置,由spring容器來管理。對於dataSource也能夠使用鏈接池,這裏就須要採用第三方dbcp或者c3p0來完成,而後又容器將dataSource交給JdbcTemplate使用。

相關文章
相關標籤/搜索