引言

如果说JdbcTemplate类是Spring Jdbc的核心类,那么execute方法算得上Spring Jdbc的核心方法了,毕竟JdbcTemplate的很多public方法内部实际上是调用execute方法实现的。

public T execute(ConnectionCallback action) throws DataAccessException

通过使用操作一个JDBC的Connection的回调操作,可以执行JDBC的数据操作,同时支持Spring的事务管理和可以将throw的SQLException转为Spring统一定义的DataAccessException。该回调函数可以返回一个对象,也可以返回结果集。

内部实现

    public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(obtainDataSource());
try {
// Create close-suppressing Connection proxy, also preparing returned Statements.
Connection conToUse = createConnectionProxy(con);
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.
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

通过查看源代码,我们可以看出ConnectionCallback需要传入一个特殊的Jdbc Connection。

首先DataSourceUtils从当前的数据源获取一个普通Connection(该Connection支持Spring的事务),然后使用createConnectionProxy对这个Connection进行了封装了,使用了JDK动态代理技术,对Connection的一些方法进行了重新处理,比如isClose方法只会返回false,执行close方法时其内部根本就没有关闭。

当抛出SQLException的时候,为了防止初始化异常转换器的时候出现的存放Connecton的池出现死锁,会优先尝试释放该Connection,如果该Connection支持事务则释放,不然则由Spring自行判断是否关闭。实际上通过查看DataSourceUtils的具体实现可以发现,该Connection是通过TransactionSynchronizationManager包装过,因此是支持Spring的事务管理的。

T execute(StatementCallback action) throws DataAccessException

通过使用操作一个JDBC的Statement的回调操作,可以执行JDBC的数据操作,同时支持Spring的事务管理和可以将throw的SQLException转为Spring统一定义的DataAccessException。该回调函数可以返回一个对象,也可以返回结果集。

内部实现

	public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null"); Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
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.
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

通过查看源代码,Connection的创建和释放就交给了DataSourceUtils,关于这个类如何创建、释放和关闭Connection,则可以看我的另一篇博客《DataUtils对Connection的获取、释放和关闭的操作学习》。

在获得Connection之后再创建Statement,并且通过applyStatementSettings方法设置Statement的fetchSize、maxRows和timeout属性。在完成了Statement的创建工作之后便是调用StatementCallback回调接口,返回执行结果。不过在返回执行结果之前需要判断是否抛出SQL Warning。如果JdbcTemplate的ignoreWarnings属性为true,即忽略警告,则仅仅在日志做debug处理,否则将会抛出SQLWarningException。

public void execute(final String sql) throws DataAccessException

内部实现

	public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback());
}

通过查看源代码,我们可以看出该方法内部实际上通过方法内部类调用了execute(StatementCallback action)方法,因此这也是为什么参数sql为final -- 内部类只能调用外部的final类型变量。

此外,该方法也对sql语句做了日志的debug处理,这样我们可以在调试的时候查看sql语句,前提是我们选择实现的日志框架要支持debug。

public T execute(PreparedStatementCreator psc, PreparedStatementCallback action)

内部实现

	public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException { Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
} Connection con = DataSourceUtils.getConnection(obtainDataSource());
PreparedStatement ps = null;
try {
ps = psc.createPreparedStatement(con);
applyStatementSettings(ps);
T result = action.doInPreparedStatement(ps);
handleWarnings(ps);
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.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

这个execute方法比较少用,主要是通过PreparedStatementCreator来获取PreparedStatement。PreparedStatementCreator接口是JdbcTemplate类使用的两个核心回调接口之一(另外一个是CallableStatementCreator)。此接口通过给定的一个Connection来创建一个PreparedStatement,去负责提供sql语句和任何必要的参数。同时为了更好的方便我们进行debug,建议PreparedStatementCreator实现类也能够继承实现SqlProvider接口。

public T execute(CallableStatementCreator csc, CallableStatementCallback action)

内部实现

	public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)
throws DataAccessException { Assert.notNull(csc, "CallableStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(csc);
logger.debug("Calling stored procedure" + (sql != null ? " [" + sql + "]" : ""));
} Connection con = DataSourceUtils.getConnection(obtainDataSource());
CallableStatement cs = null;
try {
cs = csc.createCallableStatement(con);
applyStatementSettings(cs);
T result = action.doInCallableStatement(cs);
handleWarnings(cs);
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.
if (csc instanceof ParameterDisposer) {
((ParameterDisposer) csc).cleanupParameters();
}
String sql = getSql(csc);
JdbcUtils.closeStatement(cs);
cs = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);
}
finally {
if (csc instanceof ParameterDisposer) {
((ParameterDisposer) csc).cleanupParameters();
}
JdbcUtils.closeStatement(cs);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}

这个execute方法也比较少用,主要是通过CallableStatementCreator来获取CallableStatement。与PreparedStatementCreator接口类似,CallableStatementCreator是JdbcTemplate接口另外一个的核心回调接口,同样建议也去实现SqlProvider接口。

public T execute(String sql, PreparedStatementCallback action) throws DataAccessException

该方法自定义实现了PreparedStatementCreator接口,调用了execute(PreparedStatementCreator psc, PreparedStatementCallback action)方法。

内部实现

	public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException {
return execute(new SimplePreparedStatementCreator(sql), action);
}

SimplePreparedStatementCreator的定义

	private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {

		private final String sql;

		public SimplePreparedStatementCreator(String sql) {
Assert.notNull(sql, "SQL must not be null");
this.sql = sql;
} @Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(this.sql);
} @Override
public String getSql() {
return this.sql;
}
}

public T execute(String callString, CallableStatementCallback action) throws DataAccessException

该方法自定义实现了CallableStatementCreator接口,调用了execute(String callString, CallableStatementCallback action)方法。

内部实现

	public <T> T execute(String callString, CallableStatementCallback<T> action) throws DataAccessException {
return execute(new SimpleCallableStatementCreator(callString), action);
}

SimpleCallableStatementCreator的定义

	private static class SimpleCallableStatementCreator implements CallableStatementCreator, SqlProvider {

		private final String callString;

		public SimpleCallableStatementCreator(String callString) {
Assert.notNull(callString, "Call string must not be null");
this.callString = callString;
} @Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
return con.prepareCall(this.callString);
} @Override
public String getSql() {
return this.callString;
}
}

JdbcTemplate介绍<二>的更多相关文章

  1. Lucene.Net 2.3.1开发介绍 —— 二、分词(六)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(六) Lucene.Net的上一个版本是2.1,而在2.3.1版本中才引入了Next(Token)方法重载,而ReusableStrin ...

  2. Lucene.Net 2.3.1开发介绍 —— 二、分词(五)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只 ...

  3. Lucene.Net 2.3.1开发介绍 —— 二、分词(三)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...

  4. Lucene.Net 2.3.1开发介绍 —— 二、分词(四)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyze ...

  5. Lucene.Net 2.3.1开发介绍 —— 二、分词(二)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(二) 1.2.分词的过程 1.2.1.分词器工作的过程 内置的分词器效果都不好,那怎么办?只能自己写了!在写之前当然是要先看看内置的分词 ...

  6. Lucene.Net 2.3.1开发介绍 —— 二、分词(一)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...

  7. {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm

    Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...

  8. MySQL之多表查询一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习

    MySQL之多表查询 阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都会建 ...

  9. MySQL行(记录)的详细操作一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理

    MySQL行(记录)的详细操作 阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: ...

随机推荐

  1. Vue笔记:生命周期和钩子函数

    前言 在使用vue一个多礼拜后,感觉现在还停留在初级阶段,虽然知道怎么和后端做数据交互,但是对于mounted这个挂载还不是很清楚的.放大之,对vue的生命周期不甚了解.只知道简单的使用,而不知道为什 ...

  2. ETCD&Flannel安装

    .ETCD 安装: nohup etcd --name etcd0 \ --advertise-client-urls http://172.31.24.246:2379,http://127.0.0 ...

  3. A brief introduction to per-cpu variables

    墙外通道:http://thinkiii.blogspot.com/2014/05/a-brief-introduction-to-per-cpu.html per-cpu variables are ...

  4. Storm 性能优化

    目录 场景假设 调优步骤和方法 Storm 的部分特性 Storm 并行度 Storm 消息机制 Storm UI 解析 性能优化 场景假设 在介绍 Storm 的性能调优方法之前,假设一个场景:项目 ...

  5. PHP官方网站及PHP手册

    每个人都知道php.net,我们或早或晚的都会来到这里并不断的访问它.它是PHPer的主要参考网站,拥有大量有用的信息,但是这些信息却不是那么显而易见. 比较有用的官方PHP资源: PHP官方函数手册 ...

  6. Scala面向对象编程与类型系统

    Scala支持面向对象编程, 其面向对象特性与Java有共同之处并添加了很多新的特性. 类定义 scala使用class关键字定义类: class MyComplex(real0:Double, im ...

  7. PowerDesigner连接SqlServer数据库导出表结构

    环境:PowerDesigner15 数据库sql server 2005 第一步.打开PowerDesigner ,建立一个物理数据模型,具体如下图: 第二步.新建成功之后,点击"Data ...

  8. scala中的isInstanceOf和asInstanceOf

    如果实例化了子类的对象,但是将其赋予了父类类型的变量, 在后续的过程中,又需要将父类类型的变量转换为子类类型的变量,应该如何做? Ø  首先,需要使用isInstanceOf 判断对象是否为指定类的对 ...

  9. elasticsearch6.7 05. Document APIs(7)Update By Query API

    6.Update By Query API _update_by_query 接口可以在不改变 source 的情况下对 index 中的每个文档进行更新.这对于获取新属性或其他联机映射更改很有用.以 ...

  10. javascript如何处理很多数据,类似分页切换

    需求:一个用户列表数据,如果对应列表数据大于10个,就每10个保存到二维数组,后面不足10个的依然放在二维数组尾部 用处:模拟分页,或者局部刷新 在线DEMO:戳这里 var obj=[ { &quo ...