《Spring技术内幕》学习笔记11——JdbcTemplate封装Jdbc

1.Spring中使用JdbcTemplate封装对Jdbc的支持,使用Spring JdbcTeamplate的例子如下:

(1). 假设如下SQL表中有数据username=test1,passwd=test1,address=test1 

[java]  view plain copy
  1. CREATE TABLE `login` (   
  2.   `username` varchar(10default NULL,   
  3.   `passwd` varchar(10default NULL,   
  4.   `address` varchar(10default NULL   
  5. ) ENGINE=InnoDB DEFAULT CHARSET=gb2312;  

(2).Spring配置文件中添加关于数据源和JdbcTeamplate的配置如下:

[xhtml]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >   
  3. <beans>   
  4. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">   
  5.    <property name="driverClassName">   
  6.      <value>com.mysql.jdbc.Driver</value>   
  7.    </property>   
  8.    <property name="url">   
  9.      <value>jdbc:mysql://localhost:3306/javaee</value>   
  10.    </property>   
  11.    <property name="username">   
  12.      <value>root</value>   
  13.    </property>   
  14.    <property name="password">   
  15.      <value>1234</value>   
  16.    </property>   
  17. </bean>   
  18. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">   
  19.    <property name="dataSource">   
  20.      <ref local="dataSource"/>   
  21.    </property>   
  22. </bean>   
  23. <bean id="personDAO" class="SpringJDBCSupport.ReadData.PersonDAO">   
  24.   <property name="jdbcTemplate">   
  25.     <ref local="jdbcTemplate"/>   
  26.   </property>   
  27. </bean>   
  28. </beans>  

(3).Java持久化对象如下:

[java]  view plain copy
  1. package SpringJDBCSupport.ReadData;   
  2. import com.mysql.jdbc.Driver;   
  3. public class Person {   
  4.   private String name;   
  5.   private String password;   
  6.   private String address;   
  7.   public Person(){   
  8.   }   
  9.   public Person(String name,String password,String address){   
  10.       this.name=name;   
  11.       this.password=password;   
  12.       this.address=address;   
  13.   }   
  14. public String getAddress() {   
  15.     return address;   
  16. }   
  17. public void setAddress(String address) {   
  18.     this.address = address;   
  19. }   
  20. public String getName() {   
  21.     return name;   
  22. }   
  23. public void setName(String name) {   
  24.     this.name = name;   
  25. }   
  26. public String getPassword() {   
  27.     return password;   
  28. }   
  29. public void setPassword(String password) {   
  30.     this.password = password;   
  31. }   
  32. public String toString(){   
  33.     return this.getName()+"-"+this.getPassword()+"-"+this.getAddress();   
  34. }   
  35. }  

(4).使用JdbcTeamplate的DAO如下:

[java]  view plain copy
  1. package SpringJDBCSupport.ReadData;   
  2. import java.sql.PreparedStatement;   
  3. import java.sql.ResultSet;   
  4. import java.sql.SQLException;   
  5. import java.sql.Types;   
  6. import java.util.List;   
  7. import org.springframework.jdbc.core.BatchPreparedStatementSetter;   
  8. import org.springframework.jdbc.core.JdbcTemplate;   
  9. import org.springframework.jdbc.core.RowCallbackHandler;   
  10. public class PersonDAO {   
  11.   private JdbcTemplate jdbcTemplate;   
  12. public JdbcTemplate getJdbcTemplate() {   
  13.     return jdbcTemplate;   
  14. }   
  15. public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {   
  16.     this.jdbcTemplate = jdbcTemplate;   
  17. }   
  18. public int insertPersonUseUpdate(Person person){   
  19.     String sql="insert into login values(?,?,?)";   
  20.     Object[] params=new Object[]{   
  21.             person.getName(),   
  22.             person.getPassword(),   
  23.             person.getAddress()   
  24.     };   
  25.     return this.getJdbcTemplate().update(sql,params);   
  26. }   
  27. public int insertPersonUseExecute(Person person){   
  28.     String sql="insert into login values(?,?,?)";   
  29.     Object[] params=new Object[]{   
  30.             person.getName(),   
  31.             person.getPassword(),   
  32.             person.getAddress()   
  33.     };   
  34.     int[] types=new int[]{   
  35.             Types.VARCHAR,   
  36.             Types.VARCHAR,   
  37.             Types.VARCHAR   
  38.     };   
  39.     return this.getJdbcTemplate().update(sql,params,types);   
  40. }   
  41. public int[] updatePersonUseBatchUpdate( final List persons){   
  42.     String sql="insert into login values(?,?,?)";   
  43.     BatchPreparedStatementSetter setter=null;   
  44. //使用匿名内部类,实现BatchPreparedStatementSetter接口,实现批量更新  
  45.     setter=new BatchPreparedStatementSetter(){   
  46.         public int getBatchSize(){   
  47.             return persons.size();   
  48.         }   
  49.         public void setValues(PreparedStatement ps,int index) throws SQLException{   
  50.             Person person=(Person)persons.get(index);   
  51.             ps.setString(1,person.getName());   
  52.             ps.setString(2,person.getPassword());   
  53.             ps.setString(3,person.getAddress());   
  54.         }   
  55.     };   
  56.     return this.getJdbcTemplate().batchUpdate(sql,setter);   
  57. }   
  58. public Person getPersonByRowCallbackHandler(String username){   
  59.     String sql="select * from login where username=?";   
  60.     final Person person=new Person();   
  61.     final Object params[]=new Object[]{username};   
  62. //使用匿名内部类,实现RowCallbackHandler接口,即实现查询结果集的RowMapping  
  63.     this.getJdbcTemplate().query(sql,params,new RowCallbackHandler(){   
  64.         public void processRow(ResultSet rs)throws SQLException{   
  65.             person.setName(rs.getString("username"));   
  66.             person.setPassword(rs.getString("passwd"));   
  67.             person.setAddress(rs.getString("address"));   
  68.         }   
  69.     });   
  70.     return person;   
  71. }   
  72. }  

2.Spring JdbcTemplate的工作流程:

通过1中的小例子,我们可以总结出SpringJdbcTemplate的工作流程如下:

(1).配置数据源:

Spring中,将管理数据库连接的数据源当作普通Java Bean一样在Spring IoC容器中管理,当应用使用数据源时Spring IoC容器负责初始化数据源。

(2).将数据源注入JdbcTemplate:

JdbcTemplate中dataSource属性用于注入配置的数据源,Spring IoC容器通过依赖注入将配置的数据源注入到Spring对Jdbc操作的封装类JdbcTemplate中。

(3).应用中使用JdbcTemplate:

注入了数据源的JdbcTemplate就可以在应用中使用了,应用中对数据源的增删改查等操作都可以使用JdbcTemplate对外提供的方法操作数据库。

3.JdbcTemplate处理Statement的相关方法实现:

JdbcTemplate的execute方法是JdbcTemplate的核心方法,是JdbcTemplate调用jdbc进行查询,添加,删除和更新操作的基础方法,在execute方法中,主要实现对数据库的基本操作,如:获取数据库连接;根据应用对数据库的需要创建数据库的Statement;对数据库操作进行回调;处理数据库异常;关闭数据库连接等等。JdbcTemplate中有真的Jdbc中StatementPreparedStatement和CallableStatement处理的execute方法,首先我们分析处理Statement的相关方法:

(1).处理Statement的execute方法:

[java]  view plain copy
  1. //execute方法执行的是输入的sql语句,且没有返回值的  
  2. public void execute(final String sql) throws DataAccessException {  
  3.         if (logger.isDebugEnabled()) {  
  4.             logger.debug("Executing SQL statement [" + sql + "]");  
  5.         }  
  6.     //内部类,实现类StatementCallback和SqlProvider接口,执行sql语句的回调类  
  7.         class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {  
  8.             //JdbcTemplate中真正执行输入的sql语句的地方  
  9.             public Object doInStatement(Statement stmt) throws SQLException {  
  10.                 stmt.execute(sql);  
  11.                 return null;  
  12.             }  
  13.             //获取输入的sql语句  
  14.             public String getSql() {  
  15.                 return sql;  
  16.             }  
  17.         }  
  18.         //调用通用的处理静态sql语句的execute方法  
  19.         execute(new ExecuteStatementCallback());  
  20.     }  
  21. //通用的处理静态sql语句  
  22. public <T> T execute(StatementCallback<T> action) throws DataAccessException {  
  23.         Assert.notNull(action, "Callback object must not be null");  
  24.         //根据配置的数据源获取数据库连接  
  25.         Connection con = DataSourceUtils.getConnection(getDataSource());  
  26.         Statement stmt = null;  
  27.         try {  
  28.             Connection conToUse = con;  
  29.             //如果JdbcTemplate指定了本地连接,则将获取到的数据库连接转换为本地连接  
  30.             if (this.nativeJdbcExtractor != null &&  
  31.     this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {  
  32.                 conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
  33.             }  
  34.             //创建Statement  
  35.             stmt = conToUse.createStatement();  
  36.             applyStatementSettings(stmt);  
  37.             Statement stmtToUse = stmt;  
  38.             //如果JdbcTemplate指定了本地连接,则将Statement转换为本地Statement  
  39.             if (this.nativeJdbcExtractor != null) {  
  40.                 stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);  
  41.             }  
  42.     //调用ExecuteStatementCallback类的doInStatement回调方法,执行sql语句  
  43.             T result = action.doInStatement(stmtToUse);  
  44.             handleWarnings(stmt);  
  45.             //返回执行结果  
  46.             return result;  
  47.         }  
  48.         catch (SQLException ex) {  
  49.             //产生异常,则关闭Statement  
  50.             JdbcUtils.closeStatement(stmt);  
  51.             stmt = null;  
  52.             //释放数据库连接  
  53.             DataSourceUtils.releaseConnection(con, getDataSource());  
  54.             con = null;  
  55.             //将数据库异常封装为Spring异常向调用者抛出  
  56.             throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);  
  57.         }  
  58.         finally {  
  59.             //关闭Statement,释放数据库连接  
  60.             JdbcUtils.closeStatement(stmt);  
  61.             DataSourceUtils.releaseConnection(con, getDataSource());  
  62.         }  
  63.     }  

Execute方法是JdbcTemplate执行jdbc操作的核心,其他的方法都是通过调用execute方法来操作数据库。

(2). Statement的查询方法:

JdbcTemplate处理Statement的查询方法有很多,但是其中最基本的方法源码如下:

[java]  view plain copy
  1. //静态sql的查询方法,第二个参数ResultSetExtractor是查询结果集转换器,用于处理查//询得到的结果集  
  2. public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {  
  3.         Assert.notNull(sql, "SQL must not be null");  
  4.         Assert.notNull(rse, "ResultSetExtractor must not be null");  
  5.         if (logger.isDebugEnabled()) {  
  6.             logger.debug("Executing SQL query [" + sql + "]");  
  7.         }  
  8.     //实现了StatementCallback和SqlProvider接口的内部类,用于execute方法回调  
  9.         class QueryStatementCallback implements StatementCallback<T>, SqlProvider {  
  10.             //execute方法执行查询操作时回调方法,真正执行jdbc操作的方法  
  11.             public T doInStatement(Statement stmt) throws SQLException {  
  12.                 ResultSet rs = null;  
  13.                 try {  
  14.                     //调用Statement的查询方法  
  15.                     rs = stmt.executeQuery(sql);  
  16.                     ResultSet rsToUse = rs;  
  17.                     //如果JdbcTemplate指定了本地jdbc提取器,则将查询得到的结果  
  18. //集提取为本地结果集  
  19.                     if (nativeJdbcExtractor != null) {  
  20.                         rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);  
  21.                     }  
  22.                     //使用结果集提取器对查询得到的结果集进行处理  
  23.                     return rse.extractData(rsToUse);  
  24.                 }  
  25.                 finally {  
  26.                     //关闭结果集  
  27.                     JdbcUtils.closeResultSet(rs);  
  28.                 }  
  29.             }  
  30.             //获取sql  
  31.             public String getSql() {  
  32.                 return sql;  
  33.             }  
  34.         }  
  35.         //调用处理Statement的execute方法  
  36.         return execute(new QueryStatementCallback());  
  37.     }  

(3).Statement的更新方法:

[java]  view plain copy
  1. //静态sql的更新方法  
  2.     public int update(final String sql) throws DataAccessException {  
  3.         Assert.notNull(sql, "SQL must not be null");  
  4.         if (logger.isDebugEnabled()) {  
  5.             logger.debug("Executing SQL update [" + sql + "]");  
  6.         }  
  7.     //实现了StatementCallback和SqlProvider接口的内部类,Statement  
  8. //的execute方法回调  
  9.         class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider {  
  10.             //Statement的execute方法更新操作时回调方法,真正执行jdbc操作的方法  
  11.             public Integer doInStatement(Statement stmt) throws SQLException {  
  12.                 //Statement执行jdbc的更新操作,返回影响行数  
  13.                 int rows = stmt.executeUpdate(sql);  
  14.                 if (logger.isDebugEnabled()) {  
  15.                     logger.debug("SQL update affected " + rows + " rows");  
  16.                 }  
  17.                 return rows;  
  18.             }  
  19.             public String getSql() {  
  20.                 return sql;  
  21.             }  
  22.         }  
  23.         //调用Statement的execute方法  
  24.         return execute(new UpdateStatementCallback());  
  25.     }  

通过对Statement相关处理的方法源码分析,我们可以看出execute方法是核心方法,在execute方法中,主要获取数据库连接和创建Statement,同时当执行完jdbc操作之后释放连接和资源等数据库操作的通用流程,所有的查询,更新等具体操作均是通过向execute方法传递合适的回调参数来使用execute方法中的数据库通用流程和资源,真正执行jdbc操作的方法由具体的回调内部类来实现。

4.JdbcTemplate处理PreparedStatement的相关方法实现:

 

(1).处理PreparedStatement的execute方法:

[java]  view plain copy
  1. //PreparedStatement处理sql语句的execute方法  
  2. public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException {  
  3. //将sql语句封装成为SimplePreparedStatementCreator,调用处理//PreparedStatement的方法  
  4.         return execute(new SimplePreparedStatementCreator(sql), action);  
  5.     }  
  6. //处理PreparedStatement  
  7. public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)  
  8.             throws DataAccessException {  
  9.         Assert.notNull(psc, "PreparedStatementCreator must not be null");  
  10.         Assert.notNull(action, "Callback object must not be null");  
  11.         if (logger.isDebugEnabled()) {  
  12.             String sql = getSql(psc);  
  13.             logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));  
  14.         }  
  15.         //获取数据库连接  
  16.         Connection con = DataSourceUtils.getConnection(getDataSource());  
  17.         PreparedStatement ps = null;  
  18.         try {  
  19.             Connection conToUse = con;  
  20.         //如果JdbcTemplate指定了jdbc本地提取器,则将获取到的数据库连接转换  
  21. //为本地数据库连接  
  22.             if (this.nativeJdbcExtractor != null &&  
  23.     this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {  
  24.                 conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
  25.             }  
  26.             //根据数据库连接和sql语句创建PreparedStatement  
  27.             ps = psc.createPreparedStatement(conToUse);  
  28.             applyStatementSettings(ps);  
  29.             PreparedStatement psToUse = ps;  
  30.             //如果JdbcTemplate指定了jdbc本地提取器,则将PreparedStatement转换  
  31. //为本地PreparedStatement  
  32.             if (this.nativeJdbcExtractor != null) {  
  33.                 psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);  
  34.             }  
  35.             //调用回调处理器,执行相应操作  
  36.             T result = action.doInPreparedStatement(psToUse);  
  37.             handleWarnings(ps);  
  38.             return result;  
  39.         }  
  40.         catch (SQLException ex) {  
  41.             //关闭资源,释放连接  
  42.             if (psc instanceof ParameterDisposer) {  
  43.                 ((ParameterDisposer) psc).cleanupParameters();  
  44.             }  
  45.             String sql = getSql(psc);  
  46.             psc = null;  
  47.             JdbcUtils.closeStatement(ps);  
  48.             ps = null;  
  49.             DataSourceUtils.releaseConnection(con, getDataSource());  
  50.             con = null;  
  51.             //将jdbc相关异常封装转换为Spring异常向调用者抛出  
  52.             throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);  
  53.         }  
  54.         finally {  
  55.             //清除PreparedStatement的参数  
  56.             if (psc instanceof ParameterDisposer) {  
  57.                 ((ParameterDisposer) psc).cleanupParameters();  
  58.             }  
  59.             //关闭PreparedStatement  
  60.             JdbcUtils.closeStatement(ps);  
  61.             //释放数据库连接  
  62.             DataSourceUtils.releaseConnection(con, getDataSource());  
  63.         }  
  64.     }  

(2). PreparedStatement的查询方法:

和Statement类似,JdbcTemplate中PreparedStatement的查询方法也非常多,我们以最基本的查询方法源码为例分析其处理流程:

[java]  view plain copy
  1. //PreparedStatement查询方法,第一个参数PreparedStatementCreator为封装sql语句的//类,第二个参数PreparedStatementSetter为向PreparedStatement设置参数的类,第三//个参数ResultSetExtractor为处理结果集的类  
  2. public <T> T query(  
  3.             PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)  
  4.             throws DataAccessException {  
  5.         Assert.notNull(rse, "ResultSetExtractor must not be null");  
  6.         logger.debug("Executing prepared SQL query");  
  7. //调用PreparedStatement的execute方法,第二个参数为一个实现了//PreparedStatementCallback接口的匿名内部类,被execute回调  
  8.         return execute(psc, new PreparedStatementCallback<T>() {  
  9.             //执行jdbc操作的方法  
  10.             public T doInPreparedStatement(PreparedStatement ps) throws SQLException {  
  11.                 ResultSet rs = null;  
  12.                 try {  
  13.                 //如果PreparedStatement参数不为null,则为PreparedStatement设置参数值  
  14.                     if (pss != null) {  
  15.                         pss.setValues(ps);  
  16.                     }  
  17.                     //执行jdbc查询操作  
  18.                     rs = ps.executeQuery();  
  19.                     ResultSet rsToUse = rs;  
  20.             //如果JdbcTemplate指定了本地jdbc提取器,则将查询得到的结果  
  21. //集转换为本地jdbc结果集  
  22.                     if (nativeJdbcExtractor != null) {  
  23.                         rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);  
  24.                     }  
  25.                     //使用配置的结果集处理器提取结果集数据  
  26.                     return rse.extractData(rsToUse);  
  27.                 }  
  28.                 finally {  
  29.                     //关闭结果集  
  30.                     JdbcUtils.closeResultSet(rs);  
  31.                     //清除PreparedStatement参数  
  32.                     if (pss instanceof ParameterDisposer) {  
  33.                         ((ParameterDisposer) pss).cleanupParameters();  
  34.                     }  
  35.                 }  
  36.             }  
  37.         });  
  38.     }  

(3). PreparedStatement的更新方法:

[java]  view plain copy
  1. // PreparedStatement更新方法,第二个参数PreparedStatementSetter为向//PreparedStatement设置参数的类  
  2. protected int update(final PreparedStatementCreator psc, final PreparedStatementSetter pss)  
  3.             throws DataAccessException {  
  4.         logger.debug("Executing prepared SQL update");  
  5. //调用PreparedStatement的execute方法,第二个参数为实现了//PreparedStatementCallback接口的匿名内部类,由PreparedStatement的  
  6. //execute方法回调  
  7.         return execute(psc, new PreparedStatementCallback<Integer>() {  
  8.             //真正执行jdbc操作的方法  
  9.             public Integer doInPreparedStatement(PreparedStatement ps) throws SQLException {  
  10.                 try {  
  11.                     //为PreparedStatement设置参数值  
  12.                     if (pss != null) {  
  13.                         pss.setValues(ps);  
  14.                     }  
  15.                     //PreparedStatement调用jdbc的更新操作,返回受影响的行数  
  16.                     int rows = ps.executeUpdate();  
  17.                     if (logger.isDebugEnabled()) {  
  18.                         logger.debug("SQL update affected " + rows + " rows");  
  19.                     }  
  20.                     return rows;  
  21.                 }  
  22.                 finally {  
  23.                     //清除PreparedStatement参数  
  24.                     if (pss instanceof ParameterDisposer) {  
  25.                         ((ParameterDisposer) pss).cleanupParameters();  
  26.                     }  
  27.                 }  
  28.             }  
  29.         });  
  30.     }  

通过对PreparedStatement相关处理方法的源码分析,我们可以看到PreparedStatement和Statement的处理流程基本是相同的,不同之处在于PreparedStatement需要处理设置参数值的操作。

5.JdbcTemplate处理CallableStatement的相关方法实现:

(1).处理CallableStatement的execute方法:

[java]  view plain copy
  1. //CallableStatement处理给定字符串的  
  2. public <T> T execute(String callString, CallableStatementCallback<T> action) throws DataAccessException {  
  3. //将字符串封装为SimpleCallableStatementCreator类,调用execute方法处//理CallableStatement  
  4.         return execute(new SimpleCallableStatementCreator(callString), action);  
  5.     }  
  6. //CallableStatement的execute方法,第一个参数CallableStatementCreator是封装调用字//符串的类,封装调用数据库存储过程的语句  
  7. public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)  
  8.             throws DataAccessException {  
  9.         Assert.notNull(csc, "CallableStatementCreator must not be null");  
  10.         Assert.notNull(action, "Callback object must not be null");  
  11.         if (logger.isDebugEnabled()) {  
  12.             String sql = getSql(csc);  
  13.             logger.debug("Calling stored procedure" + (sql != null ? " [" + sql  + "]" : ""));  
  14.         }  
  15.         //根据数据源获取数据库连接  
  16.         Connection con = DataSourceUtils.getConnection(getDataSource());  
  17.         CallableStatement cs = null;  
  18.         try {  
  19.             Connection conToUse = con;  
  20.             //如果JdbcTemplate指定了本地jdbc提取器,则将获取到的数据库连接转换为  
  21.             //本地数据库连接  
  22.             if (this.nativeJdbcExtractor != null) {  
  23.                 conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
  24.             }  
  25.             //根据数据库连接创建CallableStatement  
  26.             cs = csc.createCallableStatement(conToUse);  
  27.             applyStatementSettings(cs);  
  28.             CallableStatement csToUse = cs;  
  29.             //如果JdbcTemplate指定了本地jdbc提取器,则将CallableStatement转换为  
  30.             //本地CallableStatement  
  31.             if (this.nativeJdbcExtractor != null) {  
  32.                 csToUse = this.nativeJdbcExtractor.getNativeCallableStatement(cs);  
  33.             }  
  34.             //调用相应回调对象的方法,执行jdbc操作  
  35.             T result = action.doInCallableStatement(csToUse);  
  36.             handleWarnings(cs);  
  37.             return result;  
  38.         }  
  39.         catch (SQLException ex) {  
  40.             //清除CallableStatement参数  
  41.             if (csc instanceof ParameterDisposer) {  
  42.                 ((ParameterDisposer) csc).cleanupParameters();  
  43.             }  
  44.             String sql = getSql(csc);  
  45.             csc = null;  
  46.             //关闭CallableStatement  
  47.             JdbcUtils.closeStatement(cs);  
  48.             cs = null;  
  49.             //释放数据库连接  
  50.             DataSourceUtils.releaseConnection(con, getDataSource());  
  51.             con = null;  
  52.             //将jdbc异常封装为Spring异常  
  53.             throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);  
  54.         }  
  55.         finally {  
  56.             //清除CallableStatement参数  
  57.             if (csc instanceof ParameterDisposer) {  
  58.                 ((ParameterDisposer) csc).cleanupParameters();  
  59.             }  
  60.             //关闭CallableStatement  
  61.             JdbcUtils.closeStatement(cs);  
  62.             //释放数据库连接  
  63.             DataSourceUtils.releaseConnection(con, getDataSource());  
  64.         }  
  65.     }  

(2).CallableStatementcall方法:

[java]  view plain copy
  1. //CallableStatement调用数据库的存储过程  
  2. public Map<String, Object> call(CallableStatementCreator csc, List<SqlParameter> declaredParameters)  
  3.             throws DataAccessException {  
  4.         final List<SqlParameter> updateCountParameters = new ArrayList<SqlParameter>();  
  5.         final List<SqlParameter> resultSetParameters = new ArrayList<SqlParameter>();  
  6.         final List<SqlParameter> callParameters = new ArrayList<SqlParameter>();  
  7.         //遍历声明的参数  
  8.         for (SqlParameter parameter : declaredParameters) {  
  9.             //如果参数是结果参数  
  10.             if (parameter.isResultsParameter()) {  
  11.                 //如果参数是返回结果集类型,则将参数添加到结果集参数集合中  
  12.                 if (parameter instanceof SqlReturnResultSet) {  
  13.                     resultSetParameters.add(parameter);  
  14.                 }  
  15.                 //如果参数不是返回结果集类型,则将参数添加到更新数目参数集合中  
  16.                 else {  
  17.                     updateCountParameters.add(parameter);                 
  18.                 }  
  19.             }  
  20.             //如果参数不是结果参数,则将参数添加到调用参数集合中  
  21.             else {  
  22.                 callParameters.add(parameter);  
  23.             }  
  24.         }  
  25. //调用CallableStatement的execute方法,第二个参数是实现了//CallableStatementCallback接口的匿名内部类,用于回调  
  26.         return execute(csc, new CallableStatementCallback<Map<String, Object>>() {  
  27.             //真正调用jdbc操作的方法  
  28.             public Map<String, Object> doInCallableStatement(CallableStatement cs) throws SQLException {  
  29. //CallableStatement执行jdbc调用,如果是返回结果为结果集则为//ture,如果执行返回结果不是结果集则返回false  
  30.                 boolean retVal = cs.execute();  
  31.                 //获取CallableStatement执行后数据库中被更新的记录数  
  32.                 int updateCount = cs.getUpdateCount();  
  33.                 if (logger.isDebugEnabled()) {  
  34.                     logger.debug("CallableStatement.execute() returned '" + retVal + "'");  
  35.                     logger.debug("CallableStatement.getUpdateCount() returned " + updateCount);  
  36.                 }  
  37.                 //创建一个用于返回指向结果的集合  
  38.                 Map<String, Object> returnedResults = createResultsMap();  
  39.                    //如果CallableStatement执行的返回结果结果是结果集,或者  
  40. //CallableStatement执行的更新操作,数据库中有记录被更新  
  41.                 if (retVal || updateCount != -1) {  
  42.                     //将存储过程调用结果提取成结果集集合  
  43.                     returnedResults.putAll(extractReturnedResults(cs, updateCountParameters, resultSetParameters, updateCount));  
  44.                 }  
  45.     //如果CallableStatement执行的返回结果不是结果集,且不是更新操作,  
  46.             //则将存储过程的输出参数提取为结果集  
  47.                 returnedResults.putAll(extractOutputParameters(cs, callParameters));  
  48.                 return returnedResults;  
  49.             }  
  50.         });  
  51.     }  

通过上面对CallableStatement相关处理方法的源码分析我们可以看到,execute方法基本和Statement和PreparedStatement是相同的,不同之处在于CallableStatement是通过jdbc调用数据库的存储过程,对于输入输出参数的组装,以及返回结果的处理方面有些特殊处理。

6.JdbcTemplate通过DataSourceUtils获取数据库连接:

JdbcTemplate的execute方法中通过DataSourceUtils.getConnection(getDataSource());获取数据库连接,下面我们就分析DataSourceUtils类获取数据库连接的实现过程:

[java]  view plain copy
  1. //获取数据库连接的入口方法  
  2. public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {  
  3.         try {  
  4.             //通过doGetConnection方法来获取数据库连接  
  5.             return doGetConnection(dataSource);  
  6.         }  
  7.         catch (SQLException ex) {  
  8.             throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);  
  9.         }  
  10.     }  
  11. //获取数据库连接  
  12. public static Connection doGetConnection(DataSource dataSource) throws SQLException {  
  13.         Assert.notNull(dataSource, "No DataSource specified");  
  14.         //把数据库连接放到事务管理器中管理  
  15.     //通过TransactionSynchronizationManager中定义的线程局部  
  16. //变量(threadlocal)来和线程绑定数据库连接  
  17.         ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
  18.     //如果TransactionSynchronizationManager中已有与当前线程绑定的数据库连接  
  19.         if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  
  20.             //从当前线程线程局部变量中获取数据库连接  
  21.             conHolder.requested();  
  22.     //如果当前线程局部变量中没有绑定数据库连接,则为当前线程局部变量设置数据库连接  
  23.             if (!conHolder.hasConnection()) {  
  24.                 logger.debug("Fetching resumed JDBC Connection from DataSource");  
  25.                 conHolder.setConnection(dataSource.getConnection());  
  26.             }  
  27.     //直接返回TransactionSynchronizationManager线程局部变量中的数据库连接  
  28.             return conHolder.getConnection();  
  29.         }  
  30.         //如果TransactionSynchronizationManager中没有和当前线程绑定的数据  
  31. //库连接,则从Spring配置文件配置的数据源对象中获取数据库连接  
  32.         logger.debug("Fetching JDBC Connection from DataSource");  
  33.         Connection con = dataSource.getConnection();  
  34. //如果当前线程事务同步是Active的,即在注册之前可以直接使用,避免不必要  
  35. //的实例对象创建  
  36.         if (TransactionSynchronizationManager.isSynchronizationActive()) {  
  37.             logger.debug("Registering transaction synchronization for JDBC Connection");  
  38.             //在事务中使用同一个数据库连接做jdbc操作,当事务结束后,线程绑定对象  
  39. //将被同步移除   
  40.             ConnectionHolder holderToUse = conHolder;  
  41.             //如果存放数据库连接的线程局部变量为null,则重新创建一个线程局部变量  
  42.             if (holderToUse == null) {  
  43.                 holderToUse = new ConnectionHolder(con);  
  44.             }  
  45.             //如果存放数据库连接的线程局部变量不为null,则将数据库连接存放到线程  
  46. //局部变量中  
  47.             else {  
  48.                 holderToUse.setConnection(con);  
  49.             }  
  50.             //请求数据库连接  
  51.             holderToUse.requested();  
  52.             //为当前线程注册事务同步  
  53.             TransactionSynchronizationManager.registerSynchronization(  
  54.                     new ConnectionSynchronization(holderToUse, dataSource));  
  55.             //标记当前数据库连接为事务同步  
  56.             holderToUse.setSynchronizedWithTransaction(true);  
  57.             if (holderToUse != conHolder) {  
  58.             //将数据源绑定到当前线程局部变量  
  59.             TransactionSynchronizationManager.bindResource(dataSource, holderToUse);  
  60.             }  
  61.         }  
  62.         return con;  
  63.     }  

 

通过对JdbcTemplate的源码分析,我们看到Spring只是将jdbc的一些常用操作封装,将通用的获取数据库连接、创建创建Statement、关闭资源释放连接等操作封装在不同种类的execute方法中,同时调用不同的回调处理Action来具体执行jdbc操作,对jdbc熟悉的人很容易看懂这部分源码,当然Spring还对jdbc进行了一些高级的封装和扩展,例如RowMapper将结果集转换为指定对象等,这部分有兴趣可以自己研究。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值