# Spring JDBC - Author: [HuiFer](https://github.com/huifer) - 源码阅读仓库: [huifer-spring](https://github.com/huifer/spring-framework-read) ## 环境搭建 - 依赖 ```gradle compile(project(":spring-jdbc")) compile group: 'com.alibaba', name: 'druid', version: '1.1.21' compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.47' ``` - db配置 ```properties jdbc.url= jdbc.driverClass= jdbc.username= jdbc.password= ``` - 实体对象 ```JAVA public class HsLog { private Integer id; private String source; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } } ``` - DAO ```JAVA public interface HsLogDao { List findAll(); void save(HsLog hsLog); } ``` - 实现类 ```JAVA public class HsLogDaoImpl extends JdbcDaoSupport implements HsLogDao { @Override public List findAll() { return this.getJdbcTemplate().query("select * from hs_log", new HsLogRowMapper()); } @Override public void save(HsLog hsLog) { this.getJdbcTemplate().update("insert into hs_log (SOURCE) values(?)" , new Object[]{ hsLog.getSource(), } ); } class HsLogRowMapper implements RowMapper { public HsLog mapRow(ResultSet rs, int rowNum) throws SQLException { HsLog log = new HsLog(); log.setId(rs.getInt("id")); log.setSource(rs.getString("source")); return log; } } } ``` - xml ```xml ``` - 运行方法 ```JAVA public class SpringJDBCSourceCode { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("JDBC-demo.xml"); HsLogDaoImpl bean = applicationContext.getBean(HsLogDaoImpl.class); System.out.println(bean.findAll()); HsLog hsLog = new HsLog(); hsLog.setSource("jlkjll"); bean.save(hsLog); } } ``` ## 链接对象构造 `Connection con = DataSourceUtils.getConnection(obtainDataSource());` ```java public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex); } catch (IllegalStateException ex) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage()); } } ``` ### org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection ```java public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); 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(fetchConnection(dataSource)); } 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 = fetchConnection(dataSource); // 当前线程支持同步 if (TransactionSynchronizationManager.isSynchronizationActive()) { try { // 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); } } catch (RuntimeException ex) { // Unexpected exception from external delegation call -> close Connection and rethrow. releaseConnection(con, dataSource); throw ex; } } return con; } ``` ## 释放资源 `releaseConnection(con, dataSource);` - `org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection` ```JAVA public static void releaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) { try { doReleaseConnection(con, dataSource); } catch (SQLException ex) { logger.debug("Could not close JDBC Connection", ex); } catch (Throwable ex) { logger.debug("Unexpected exception on closing JDBC Connection", ex); } } ``` ```java public static void doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException { if (con == null) { return; } if (dataSource != null) { ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && connectionEquals(conHolder, con)) { // It's the transactional Connection: Don't close it. // 连接数-1 conHolder.released(); return; } } // 处理其他情况 doCloseConnection(con, dataSource); } ``` ### org.springframework.transaction.support.ResourceHolderSupport 链接数 ```JAVA /** * Increase the reference count by one because the holder has been requested * (i.e. someone requested the resource held by it). */ public void requested() { this.referenceCount++; } /** * Decrease the reference count by one because the holder has been released * (i.e. someone released the resource held by it). */ public void released() { this.referenceCount--; } ``` ## 查询解析 ### org.springframework.jdbc.core.JdbcTemplate ```XML ``` - 从配置中可以知道 JdbcTemplate 需要 dataSource 属性, 就从这里开始讲起 - `org.springframework.jdbc.support.JdbcAccessor.setDataSource`, 这段代码就只做了赋值操作(依赖注入) ```java public void setDataSource(@Nullable DataSource dataSource) { this.dataSource = dataSource; } ``` - 下面`hsLogDao`也是依赖注入本篇不做详细讲述。 ### org.springframework.jdbc.core.JdbcTemplate#query(java.lang.String, org.springframework.jdbc.core.RowMapper) ```java @Override public List findAll() { return this.getJdbcTemplate().query("select * from hs_log", new HsLogRowMapper()); } ``` ```java @Override @Nullable public T query(final String sql, final ResultSetExtractor rse) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); if (logger.isDebugEnabled()) { logger.debug("Executing SQL query [" + sql + "]"); } /** * Callback to execute the query. */ class QueryStatementCallback implements StatementCallback, SqlProvider { @Override @Nullable public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { // 执行sql rs = stmt.executeQuery(sql); // 1. org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData return rse.extractData(rs); } finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new QueryStatementCallback()); } ``` ```java @Override @Nullable public T execute(StatementCallback 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()); } } ``` ```JAVA @Override public List extractData(ResultSet rs) throws SQLException { List results = (this.rowsExpected > 0 ? new ArrayList<>(this.rowsExpected) : new ArrayList<>()); int rowNum = 0; while (rs.next()) { // 调用自定义的 rowMapper 进行数据处理 T t = this.rowMapper.mapRow(rs, rowNum++); results.add(t); } return results; } ``` ![image-20200109150841916](/images/spring/image-20200109150841916.png) 这样就可以获取到了 方法`result`没有什么操作直接返回即可 ```java private static T result(@Nullable T result) { Assert.state(result != null, "No result"); return result; } ``` ## 插入解析 ```java @Override public void save(HsLog hsLog) { this.getJdbcTemplate().update("insert into hs_log (SOURCE) values(?)" , new Object[]{ hsLog.getSource(), } ); } ``` `org.springframework.jdbc.core.JdbcTemplate#update(org.springframework.jdbc.core.PreparedStatementCreator, org.springframework.jdbc.core.PreparedStatementSetter)` ```java protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss) throws DataAccessException { logger.debug("Executing prepared SQL update"); return updateCount(execute(psc, ps -> { try { if (pss != null) { // 设置请求参数 pss.setValues(ps); } int rows = ps.executeUpdate(); if (logger.isTraceEnabled()) { logger.trace("SQL update affected " + rows + " rows"); } return rows; } finally { if (pss instanceof ParameterDisposer) { ((ParameterDisposer) pss).cleanupParameters(); } } })); } ```