首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > 其他数据库 >

Spring技术黑幕——深入解析Spring架构与设计原理(三)数据库的操作实现

2012-11-01 
Spring技术内幕——深入解析Spring架构与设计原理(三)数据库的操作实现最近事情实在是比较多,没有及时更新帖

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());}}

在使用数据库的时候,有一个很重要的地方就是对数据库连接的管理,在这里,是由DataSourceUtils来完成的。Spring通过这个辅助类来对数据的Connection进行管理。比如通过它来完成打开和关闭Connection等操作。DataSourceUtils对这些数据库Connection管理的实现, 如以下代码所示。
//这是取得数据库连接的调用,实现是通过调用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;}


关于数据库操作类RDBMS
从JdbcTemplate中,我们看到,他提供了许多简单查询和更新的功能。但是,如果需要更高层次的抽象,以及更面向对象的方法来访问数据库,Spring为我们提供了org.springframework.jdbc.object包,里面包含了SqlQuery、SqlMappingQuery、SqlUpdate和StoredProcedure等类,这些类都是Spring JDBC应用程序可以使用的。但要注意,在使用这些类时需要为它们配置好JdbcTemplate作为其基本的操作实现,因为在它们的功能实现中,对数据库操作的那部分实现基本上还是依赖于JdbcTemplate来完成的。

比如,对MappingSqlQuery使用的过程,是非常简洁的;在设计好数据的映射代码之后,查询得到的记录已经按照前面的设计转换为对象List了,一条查询记录对应于一个数据对象,可以把数据库的数据记录直接映射成Java对象在程序中使用,同时又可避免使用第三方ORM工具的配置,对于简单的数据映射场合是非常方便的;在mapRow方法的实现中提供的数据转换规则,和我们使用Hibernate时,Hibernate的hbm文件起到的作用是非常类似的。这个MappingSqlQuery需要的对设置进行compile,这些compile是这样完成的,如以下代码所示:
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);}


在Spring对JDBC的操作中,基本上是对JDBC/Hibernate基础上API的封装。这些封装可以直接使用,也可以在IoC容器中配置好了再使用,当结合IoC容器的基础上进行使用的时候,可以看到许多和事务管理相关的处理部分,都是非常值得学习的,在那里,可以看到对数据源的管理 - Hibernate中session的管理,与线程的结合等等。@Testpublic void update() {String sql = "update user set name='newValue' where id=5";jdbcTemplate.update(sql);}

@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);}


麻烦指教一下,谢谢! 14 楼 jiwenke 2009-12-20   把异常贴出来看看,一起讨论讨论? 15 楼 godspeed_yjx 2009-12-26   楼主,能不能把你的这个系列的文章做一个目录出来啊,我想系统的看一下。 16 楼 jiwenke 2010-01-03   godspeed_yjx 写道楼主,能不能把你的这个系列的文章做一个目录出来啊,我想系统的看一下。
以这些文章为底子,写的书,在http://hzbook.group.iteye.com/group/blog/560085有一些介绍,上面有目录。


热点排行