`
maosheng
  • 浏览: 552641 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring JdbcTemplate源码解析

阅读更多
在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,有许多类似JdbcTemplate的模板,比如HibernateTemplate等等。

一般而言这种Template中都是通过回调函数CallBack类的使用来完成功能的,客户需要在回调接口中实现自己需要的定制行为,比如使用客户想要用的SQL语句等。不过往往Spring通过这种回调函数的实现已经为我们提供了许多现成的方法供客户使用。

一般来说回调函数的用法采用匿名类的方式来实现,比如:

JdbcTemplate = new JdbcTemplate(datasource); 

jdbcTemplate.execute(new CallBack(){ 

       public CallbackInterfacedoInAction(){
        //用户定义的代码或者说Spring替我们实现的代码
         .......
        }


在模板中嵌入的是需要客户化的代码,由Spring来作或者需要客户程序亲自动手完成。下面让我们具体看看在JdbcTemplate中的代码是怎样完成使命的,我们举JdbcTemplate.execute()为例,这个方法是在JdbcTemplate中被其他方法调用的基本方法之一,客户程序往往用这个方法来执行基本的SQL语句:
Java代码 
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
                  //这里得到数据库联接
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null) {
// Extract native JDBC Connection, castable to OracleConnection or the like.
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
else {
// Create close-suppressing Connection proxy, also preparing returned Statements.
conToUse = createConnectionProxy(con);
}

                           //这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.

                           //如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
                           //最后不管怎样都会把数据库连接释放
DataSourceUtils.releaseConnection(con, getDataSource());
}
}


/**
* Generic callback interface for code that operates on a JDBC Connection.
* Allows to execute any number of operations on a single Connection,
* using any type and number of Statements.
*
* <p>This is particularly useful for delegating to existing data access code
* that expects a Connection to work on and throws SQLException. For newly
* written code, it is strongly recommended to use JdbcTemplate's more specific
* operations, for example a <code>query</code> or <code>update</code> variant.
*
* @author Juergen Hoeller
* @since 1.1.3
* @see JdbcTemplate#execute(ConnectionCallback)
* @see JdbcTemplate#query
* @see JdbcTemplate#update
*/
public interface ConnectionCallback<T> {

/**
* Gets called by <code>JdbcTemplate.execute</code> with an active JDBC
* Connection. Does not need to care about activating or closing the
* Connection, or handling transactions.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* JDBC connection with its transactional semantics. If JdbcTemplate is
* configured to use a JTA-aware DataSource, the JDBC Connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback, i.e.
* a domain object or a collection of domain objects. Note that there's special
* support for single step actions: see <code>JdbcTemplate.queryForObject</code>
* etc. A thrown RuntimeException is treated as application exception:
* it gets propagated to the caller of the template.
*
* @param con active JDBC Connection
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by a JDBC method, to be auto-converted
* to a DataAccessException by a SQLExceptionTranslator
* @throws DataAccessException in case of custom exceptions
* @see JdbcTemplate#queryForObject(String, Class)
* @see JdbcTemplate#queryForRowSet(String)
*/
T doInConnection(Connection con) throws SQLException, DataAccessException;

}


对于JdbcTemplate中给出的其他方法,比如query,update,execute等的实现,我们看看query():

/**
* Query using a prepared statement, allowing for a PreparedStatementCreator
* and a PreparedStatementSetter. Most other query methods use this method,
* but application code will always work with either a creator or a setter.
* @param psc Callback handler that can create a PreparedStatement given a
* Connection
* @param pss object that knows how to set values on the prepared statement.
* If this is null, the SQL will be assumed to contain no bind parameters.
* @param rse object that will extract results.
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if there is any problem
*/
public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {

Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query");

                  //这里调用了我们上面看到的execute()基本方法,然而这里的回调实现是Spring为我们完成的查询过程

return execute(psc, new PreparedStatementCallback<T>() {
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {

                                     //准备查询结果集
ResultSet rs = null;
try {
                                              //这里配置SQL参数
if (pss != null) {
pss.setValues(ps);
}
                                             //这里执行的SQL查询
rs = ps.executeQuery();
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
                                              //返回需要的记录集合
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}

辅助类DataSourceUtils来用来对数据库连接进行管理的主要工具,比如打开和关闭数据库连接等基本操作:

/**
* Obtain a Connection from the given DataSource. Translates SQLExceptions into
* the Spring hierarchy of unchecked generic data access exceptions, simplifying
* calling code and making any exception that is thrown more meaningful.
* <p>Is aware of a corresponding Connection bound to the current thread, for example
* when using {@link DataSourceTransactionManager}. Will bind a Connection to the
* thread if transaction synchronization is active, e.g. when running within a
* {@link org.springframework.transaction.jta.JtaTransactionManager JTA} transaction).
* @param dataSource the DataSource to obtain Connections from
* @return a JDBC Connection from the given DataSource
* @throws org.springframework.jdbc.CannotGetJdbcConnectionException
* if the attempt to get a Connection failed
* @see #releaseConnection
*/
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}


/**
* Actually obtain a JDBC Connection from the given DataSource.
* Same as {@link #getConnection}, but throwing the original SQLException.
* <p>Is aware of a corresponding Connection bound to the current thread, for example
* when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread
* if transaction synchronization is active (e.g. if in a JTA transaction).
* <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.
* @param dataSource the DataSource to obtain Connections from
* @return a JDBC Connection from the given DataSource
* @throws SQLException if thrown by JDBC methods
* @see #doReleaseConnection
*/
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(dataSource.getConnection());
}
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 = 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;
}



那我们实际的DataSource对象是怎样得到的?很清楚我们需要在上下文中进行配置:它作为
JdbcTemplate父类JdbcAccessor的属性存在:

/**
* Base class for {@link org.springframework.jdbc.core.JdbcTemplate} and
* other JDBC-accessing DAO helpers, defining common properties such as
* DataSource and exception translator.
*
* <p>Not intended to be used directly.
* See {@link org.springframework.jdbc.core.JdbcTemplate}.
*
* @author Juergen Hoeller
* @since 28.11.2003
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public abstract class JdbcAccessor implements InitializingBean {

/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());

private DataSource dataSource;

private SQLExceptionTranslator exceptionTranslator;

private boolean lazyInit = true;


/**
* Set the JDBC DataSource to obtain connections from.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

/**
* Return the DataSource used by this template.
*/
public DataSource getDataSource() {
return this.dataSource;
}

     .......

}
分享到:
评论

相关推荐

    Spring Boot实战与原理分析视频课程包含14-18

    --多种数据源的配置、JdbcTemplate、事务的处理 20 Spring Boot AOP 21 Spring Boot Starter18:31 --快速构建自定义的Spring Boot Starter 22 Spring Boot 日志30:58 --演示了如何在Spring Boot里面使用日志配置...

    【Spring源码解析】事务流程解析

    1、定义 以方法为单位,进行事务控制;抛出异常,事务回滚。最小的执行单位为方法。决定执行成败是通过是否抛出异常来判断的,抛出异常即执行失败。 2、代码 (1) mysql建表语句 CREATE TABLE `t_student` ( ...

    Spring中文帮助文档

    2.6.4. 将Spring 应用程序上下文部署为JCA adapter 2.6.5. 计划任务 2.6.6. 对Java 5 (Tiger) 支持 2.7. 移植到Spring 2.5 2.7.1. 改变 2.8. 更新的样例应用 2.9. 改进的文档 I. 核心技术 3. IoC(控制反转)...

    sping boot 实战视频教程(百度云+源码+视频)

    03、Spring Boot完美使用FastJson解析JSON数据_高清.mp4 04、Spring Boot热部署(springloader)_高清.mp4 05、springboot + devtools(热部署)_标清.flv 06、Spring Boot JPAHibernateSpring Data概念_标清.flv ...

    Spring高级之注解驱动开发视频教程

    n 源码分析-自定义JdbcTemplate n 设计模式-RowMapper的策略模式 n 高级应用-NamedParameterJdbcTemplate的使用 n 源码分析-TransactionTemplate n 源码分析-DataSourceUtils n 源码分析-...

    Spring-Reference_zh_CN(Spring中文参考手册)

    11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口 11.2.6. 执行SQL语句 11.2.7. 执行查询 11.2.8. 更新...

    Spring 2.0 开发参考手册

    11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口 11.2.6. 执行SQL语句 11.2.7. 执行查询 11.2.8. ...

    spring chm文档

    11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口 11.2.6. 执行SQL语句 11.2.7. 执行查询 11.2.8. ...

    Spring API

    2. Spring 2.0和 2.5的新特性 2.1. 简介 2.2. 控制反转(IoC)容器 2.2.1. 新的bean作用域 2.2.2. 更简单的XML配置 2.2.3. 可扩展的XML编写 2.2.4. Annotation(注解)驱动配置 2.2.5. 在classpath中自动搜索组件...

    springboot demo 合集(如何没有积分可以私信)

    Spring Boot干货系列:(八)数据存储篇-SQL关系型数据库之JdbcTemplate的使用:chapter8 Spring Boot干货系列:(九)数据存储篇-SQL关系型数据库之MyBatis的使用:chapter9,chapter9-2 Spring Boot干货系列:(十)...

Global site tag (gtag.js) - Google Analytics