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

关于Spring JDBC
还是从Spring JDBC说起吧,虽然现在应用很多都是直接使用Hibernate或者其他的ORM工具。但JDBC毕竟还是很基本的,其中的JdbcTemplate就是我们经常使用的,比如JDBCTemplate的execute方法,就是一个基本的方法,在这个方法的实现中,可以看到对数据库操作的基本过程。
Java代码 收藏代码
  1. //execute方法执行的是输入的sql语句
  2. publicvoidexecute(finalStringsql)throwsDataAccessException{
  3. if(logger.isDebugEnabled()){
  4. logger.debug("ExecutingSQLstatement["+sql+"]");
  5. }
  6. classExecuteStatementCallbackimplementsStatementCallback<Object>,SqlProvider{
  7. publicObjectdoInStatement(Statementstmt)throwsSQLException{
  8. stmt.execute(sql);
  9. returnnull;
  10. }
  11. publicStringgetSql(){
  12. returnsql;
  13. }
  14. }
  15. execute(newExecuteStatementCallback());
  16. }
  17. //这是使用java.sql.Statement处理静态SQL语句的方法
  18. public<T>Texecute(StatementCallback<T>action)throwsDataAccessException{
  19. Assert.notNull(action,"Callbackobjectmustnotbenull");
  20. //这里取得数据库的Connection,这个数据库的Connection已经在Spring的事务管理之下
  21. Connectioncon=DataSourceUtils.getConnection(getDataSource());
  22. Statementstmt=null;
  23. try{
  24. ConnectionconToUse=con;
  25. if(this.nativeJdbcExtractor!=null&&
  26. this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()){
  27. conToUse=this.nativeJdbcExtractor.getNativeConnection(con);
  28. }
  29. //创建Statement
  30. stmt=conToUse.createStatement();
  31. applyStatementSettings(stmt);
  32. StatementstmtToUse=stmt;
  33. if(this.nativeJdbcExtractor!=null){
  34. stmtToUse=this.nativeJdbcExtractor.getNativeStatement(stmt);
  35. }
  36. //这里调用回调函数
  37. Tresult=action.doInStatement(stmtToUse);
  38. handleWarnings(stmt);
  39. returnresult;
  40. }
  41. catch(SQLExceptionex){
  42. //ReleaseConnectionearly,toavoidpotentialconnectionpooldeadlock
  43. //inthecasewhentheexceptiontranslatorhasn'tbeeninitializedyet.
  44. //如果捕捉到数据库异常,把数据库Connection释放,同时抛出一个经过Spring转换过的Spring数据库异常
  45. //Spring做了一项有意义的工作,就是把这些数据库异常统一到自己的异常体系里了
  46. JdbcUtils.closeStatement(stmt);
  47. stmt=null;
  48. DataSourceUtils.releaseConnection(con,getDataSource());
  49. con=null;
  50. throwgetExceptionTranslator().translate("StatementCallback",getSql(action),ex);
  51. }
  52. finally{
  53. JdbcUtils.closeStatement(stmt);
  54. //释放数据库connection
  55. DataSourceUtils.releaseConnection(con,getDataSource());
  56. }
  57. }

在使用数据库的时候,有一个很重要的地方就是对数据库连接的管理,在这里,是由DataSourceUtils来完成的。Spring通过这个辅助类来对数据的Connection进行管理。比如通过它来完成打开和关闭Connection等操作。DataSourceUtils对这些数据库Connection管理的实现, 如以下代码所示。
Java代码 收藏代码
  1. //这是取得数据库连接的调用,实现是通过调用doGetConnection完成的,这里执行了异常的转换操作
  2. publicstaticConnectiongetConnection(DataSourcedataSource)throwsCannotGetJdbcConnectionException{
  3. try{
  4. returndoGetConnection(dataSource);
  5. }
  6. catch(SQLExceptionex){
  7. thrownewCannotGetJdbcConnectionException("CouldnotgetJDBCConnection",ex);
  8. }
  9. }
  10. publicstaticConnectiondoGetConnection(DataSourcedataSource)throwsSQLException{
  11. Assert.notNull(dataSource,"NoDataSourcespecified");
  12. //把对数据库的Connection放到事务管理中进行管理,这里使用TransactionSynchronizationManager中定义的ThreadLocal变量来和线程绑定数据库连接
  13. //如果在TransactionSynchronizationManager中已经有与当前线程绑定数据库连接,那就直接取出来使用
  14. ConnectionHolderconHolder=(ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
  15. if(conHolder!=null&&(conHolder.hasConnection()||conHolder.isSynchronizedWithTransaction())){
  16. conHolder.requested();
  17. if(!conHolder.hasConnection()){
  18. logger.debug("FetchingresumedJDBCConnectionfromDataSource");
  19. conHolder.setConnection(dataSource.getConnection());
  20. }
  21. returnconHolder.getConnection();
  22. }
  23. //Elseweeithergotnoholderoranemptythread-boundholderhere.
  24. //这里得到需要的数据库Connection,在Bean配置文件中定义好的,
  25. //同时最后把新打开的数据库Connection通过TransactionSynchronizationManager和当前线程绑定起来。
  26. logger.debug("FetchingJDBCConnectionfromDataSource");
  27. Connectioncon=dataSource.getConnection();
  28. if(TransactionSynchronizationManager.isSynchronizationActive()){
  29. logger.debug("RegisteringtransactionsynchronizationforJDBCConnection");
  30. //UsesameConnectionforfurtherJDBCactionswithinthetransaction.
  31. //Thread-boundobjectwillgetremovedbysynchronizationattransactioncompletion.
  32. ConnectionHolderholderToUse=conHolder;
  33. if(holderToUse==null){
  34. holderToUse=newConnectionHolder(con);
  35. }
  36. else{
  37. holderToUse.setConnection(con);
  38. }
  39. holderToUse.requested();
  40. TransactionSynchronizationManager.registerSynchronization(
  41. newConnectionSynchronization(holderToUse,dataSource));
  42. holderToUse.setSynchronizedWithTransaction(true);
  43. if(holderToUse!=conHolder){
  44. TransactionSynchronizationManager.bindResource(dataSource,holderToUse);
  45. }
  46. }
  47. returncon;
  48. }


关于数据库操作类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是这样完成的,如以下代码所示:
Java代码 收藏代码
  1. protectedfinalvoidcompileInternal(){
  2. //这里是对参数的compile过程,所有的参数都在getDeclaredParameters里面,生成了一个PreparedStatementCreatorFactory
  3. this.preparedStatementFactory=newPreparedStatementCreatorFactory(getSql(),getDeclaredParameters());
  4. this.preparedStatementFactory.setResultSetType(getResultSetType());
  5. this.preparedStatementFactory.setUpdatableResults(isUpdatableResults());
  6. this.preparedStatementFactory.setReturnGeneratedKeys(isReturnGeneratedKeys());
  7. if(getGeneratedKeysColumnNames()!=null){
  8. this.preparedStatementFactory.setGeneratedKeysColumnNames(getGeneratedKeysColumnNames());
  9. }
  10. his.preparedStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
  11. onCompileInternal();
  12. }
在执行查询时,执行的实际上是SqlQuery的executeByNamedParam方法,这个方法需要完成的工作包括配置SQL语句,配置数据记录到数据对象的转换的RowMapper,然后使用JdbcTemplate来完成数据的查询,并启动数据记录到Java数据对象的转换,如以下代码所示:
Java代码 收藏代码
  1. publicList<T>executeByNamedParam(Map<String,?>paramMap,Mapcontext)throwsDataAccessException{
  2. validateNamedParameters(paramMap);
  3. //得到需要执行的SQL语句
  4. ParsedSqlparsedSql=getParsedSql();
  5. MapSqlParameterSourceparamSource=newMapSqlParameterSource(paramMap);
  6. StringsqlToUse=NamedParameterUtils.substituteNamedParameters(parsedSql,paramSource);
  7. //配置好SQL语句需要的Parameters及rowMapper,这个rowMapper完成数据记录到对象的转换
  8. Object[]params=NamedParameterUtils.buildValueArray(parsedSql,paramSource,getDeclaredParameters());
  9. RowMapper<T>rowMapper=newRowMapper(params,context);
  10. //我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是非常基本的操作类
  11. returngetJdbcTemplate().query(newPreparedStatementCreator(sqlToUse,params),rowMapper);
  12. }


在Spring对JDBC的操作中,基本上是对JDBC/Hibernate基础上API的封装。这些封装可以直接使用,也可以在IoC容器中配置好了再使用,当结合IoC容器的基础上进行使用的时候,可以看到许多和事务管理相关的处理部分,都是非常值得学习的,在那里,可以看到对数据源的管理 - Hibernate中session的管理,与线程的结合等等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值