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