JdbcTemplate介绍<二>
引言
如果说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介绍<二>的更多相关文章
- Lucene.Net 2.3.1开发介绍 —— 二、分词(六)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(六) Lucene.Net的上一个版本是2.1,而在2.3.1版本中才引入了Next(Token)方法重载,而ReusableStrin ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(五)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(五) 2.1.3 二元分词 上一节通过变换查询表达式满足了需求,但是在实际应用中,如果那样查询,会出现另外一个问题,因为,那样搜索,是只 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(三)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(四)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(四) 2.1.2 可以使用的内置分词 简单的分词方式并不能满足需求.前文说过Lucene.Net内置分词中StandardAnalyze ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(二)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(二) 1.2.分词的过程 1.2.1.分词器工作的过程 内置的分词器效果都不好,那怎么办?只能自己写了!在写之前当然是要先看看内置的分词 ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(一)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...
- {Django基础十之Form和ModelForm组件}一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 ModelForm
Django基础十之Form和ModelForm组件 本节目录 一 Form介绍 二 Form常用字段和插件 三 From所有内置字段 四 字段校验 五 Hook钩子方法 六 进阶补充 七 Model ...
- MySQL之多表查询一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习
MySQL之多表查询 阅读目录 一 介绍 二 多表连接查询 三 符合条件连接查询 四 子查询 五 综合练习 一 介绍 本节主题 多表连接查询 复合条件连接查询 子查询 首先说一下,我们写项目一般都会建 ...
- MySQL行(记录)的详细操作一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理
MySQL行(记录)的详细操作 阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: ...
随机推荐
- Linux 学习手记(1):命令行BASH的基本操作
1. Shell 是什么 Shell(壳)是用户与操作系统底层(通常是内核)之间交互的中介程序,负责将用户指令.操作传递给操作系统底层. Shell一般分为:图形化Shell(GUI).命令行Shel ...
- .Net Core Cors中间件解析
一.同源策略和资源跨域共享 1.同源策略 同源策略,它是由Netscape提出的一个著名的安全策略.现在所有支持JavaScript 的浏览器都会使用这个策略.所谓同源是指,域名,协议,端口相同. 1 ...
- 在 浏览器中调用外接设备— —手写板 【win10 x64 手动注册ocx控件的方法】
PPAXSignToolSDK.ocx 浏览器下使用手写板时调用的控件,使用前必须先注册,,不然浏览器下版本无法正常工作. ocx 控件在安装包运行时会自动注册,如果安装包没有注册成功,需要进行手动注 ...
- JSONP和HttpClient的区别
JSONP的特点: 1>JSONP可以解决主流浏览器的跨域问题 2>需要通过三步实现跨域/javascript-src开放策略/回调函数/数据封装 3>JSONPqingqiu是游浏 ...
- 【Java基础】12、java中方法的参数传递机制
问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...
- websocket学习和群聊实现
WebSocket协议可以实现前后端全双工通信,从而取代浪费资源的长轮询.在此协议的基础上,可以实现前后端数据.多端数据,真正的实时响应.在学习WebSocket的过程中,实现了一个简化版群聊,过程和 ...
- 《移山之道:VSTS软件开发指南》读书笔记
这两天看了<移山之道:VSTS软件开发指南>,对团队软件开发又有了新的认识.也许对于我们这些软件开发的新手来说,最重要的是具体技术与应用框架,但读了这本书后我感觉到,实际团队项目中工具的使 ...
- 【读书笔记】iOS-软件测试与iOS测试
一,软件测试的类型. 1.软件测试按照测试类型,可以划分为:单元测试,集成测试和系统测试. 2.单元测试是指对软件系统中最小可测试单元进行的检查和验证. 3.集成测试,在iOS软件开发中,集成测试主要 ...
- PHP会话(Session)实现用户登陆功能 转自#落人间#
对比起 Cookie,Session 是存储在服务器端的会话,相对安全,并且不像 Cookie 那样有存储长度限制,本文简单介绍 Session 的使用. 由于 Session 是以文本文件形式存储在 ...
- 《Inside C#》笔记(十五) 非托管代码 上
为了保证向后兼容性,C#和.NET可以通过非托管的方式运行旧代码.非托管代码是指没有被.NET运行时管控的代码.非托管代码主要包括:平台调用服务(PlatformInvocation Services ...