Spring技术内幕——深入解析Spring架构与设计原理(三)数据库的操作实现
最近事情实在是比较多,没有及时更新帖子,还望大家见谅啊。今天,一起讨论讨论Spring JDBC的实现吧。
关于Spring JDBC
还是从Spring JDBC说起吧,虽然现在应用很多都是直接使用Hibernate或者其他的ORM工具。但JDBC毕竟还是很基本的,其中的JdbcTemplate就是我们经常使用的,比如JDBCTemplate的execute方法,就是一个基本的方法,在这个方法的实现中,可以看到对数据库操作的基本过程。
//execute方法执行的是输入的sql语句public void execute(final String sql) throws DataAccessException {if (logger.isDebugEnabled()) {logger.debug("Executing SQL statement [" + sql + "]");}class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {public Object doInStatement(Statement stmt) throws SQLException {stmt.execute(sql);return null;}public String getSql() {return sql;}}execute(new ExecuteStatementCallback());}//这是使用java.sql.Statement处理静态SQL语句的方法public <T> T execute(StatementCallback<T> action) throws DataAccessException {Assert.notNull(action, "Callback object must not be null");//这里取得数据库的Connection,这个数据库的Connection已经在Spring的事务管理之下Connection con = DataSourceUtils.getConnection(getDataSource());Statement stmt = null;try {Connection conToUse = con;if (this.nativeJdbcExtractor != null &&this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {conToUse = this.nativeJdbcExtractor.getNativeConnection(con);}//创建Statementstmt = 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) {// Release Connection early, to avoid potential connection pool deadlock// in the case when the exception translator hasn't been initialized yet.//如果捕捉到数据库异常,把数据库Connection释放,同时抛出一个经过Spring转换过的Spring数据库异常//Spring做了一项有意义的工作,就是把这些数据库异常统一到自己的异常体系里了JdbcUtils.closeStatement(stmt);stmt = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);}finally {JdbcUtils.closeStatement(stmt);//释放数据库connectionDataSourceUtils.releaseConnection(con, getDataSource());}}//这是取得数据库连接的调用,实现是通过调用doGetConnection完成的,这里执行了异常的转换操作public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {try {return doGetConnection(dataSource);}catch (SQLException ex) {throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);}}public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");//把对数据库的Connection放到事务管理中进行管理,这里使用TransactionSynchronizationManager中定义的ThreadLocal变量来和线程绑定数据库连接//如果在TransactionSynchronizationManager中已经有与当前线程绑定数据库连接,那就直接取出来使用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.// 这里得到需要的数据库Connection,在Bean配置文件中定义好的,// 同时最后把新打开的数据库Connection通过TransactionSynchronizationManager和当前线程绑定起来。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;}protected final void compileInternal() {//这里是对参数的compile过程,所有的参数都在getDeclaredParameters里面,生成了一个PreparedStatementCreatorFactorythis.preparedStatementFactory = new PreparedStatementCreatorFactory(getSql(), getDeclaredParameters());this.preparedStatementFactory.setResultSetType(getResultSetType());this.preparedStatementFactory.setUpdatableResults(isUpdatableResults());this.preparedStatementFactory.setReturnGeneratedKeys(isReturnGeneratedKeys());if (getGeneratedKeysColumnNames() != null) {this.preparedStatementFactory.setGeneratedKeysColumnNames(getGeneratedKeysColumnNames());}this.preparedStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());onCompileInternal();}在执行查询时,执行的实际上是SqlQuery的executeByNamedParam方法,这个方法需要完成的工作包括配置SQL语句,配置数据记录到数据对象的转换的RowMapper,然后使用JdbcTemplate来完成数据的查询,并启动数据记录到Java数据对象的转换,如以下代码所示:public List<T> executeByNamedParam(Map<String, ?> paramMap, Map context) throws DataAccessException {validateNamedParameters(paramMap);//得到需要执行的SQL语句ParsedSql parsedSql = getParsedSql();MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap);String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);//配置好SQL语句需要的Parameters及rowMapper,这个rowMapper完成数据记录到对象的转换Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters());RowMapper<T> rowMapper = newRowMapper(params, context);//我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是非常基本的操作类 return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, params), rowMapper);}@Testpublic void update() {String sql = "update User set name=:name where id=:id";Map<String, Object> paramMap = new HashMap<String, Object>();paramMap.put("id", 5);paramMap.put("name", "newValue");int i = template.update(sql, paramMap);System.out.println(i);}