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数据操作: ...
随机推荐
- Http怎么处理长连接
http协议中有和keep alive特性,这个在http1.1中有, 可以保持浏览器和服务器之间保持着长连接,http本身是无连接的协议, 通过tcp实现数据的传输,处理长连接要注意什么时候数据服务 ...
- leetcode — valid-parentheses
import java.util.Stack; /** * Source : https://oj.leetcode.com/problems/valid-parentheses/ * * Creat ...
- 菜鸟入门【ASP.NET Core】13:Individual authentication 模板、EF Core Migration
Individual authentication 模板 我们首先用VSCode新建一个mvc的网站,这个网站创立的时候回自动为我们创建Identuty Core以及EF Core的代码示例,我们可以 ...
- Mysql常用函数总结(二)
有的时候们需要了解当前的时间,这时候我们就可以调用时间函数了.下面就是一些常用的时间函数下面来说一下DATE_FORMAT(date,format):format可以有以下格式符:举个例子吧! mys ...
- 【Java基础】12、java中方法的参数传递机制
问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...
- 迭代器模式(Iterator)
1.概念 迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示,属于行为模式的一种 2.模式结构 抽象迭代器(Iterator):此抽象角色定义出遍历元素所需的接口 具体 ...
- SSM(Spring+SpringMvc+Mybatis)整合笔记
1.使用开发工具 jdk1.8 eclipse Tomcat7.0 MySql 2.创建数据库和表,由于重点是整合,所以数据库就随意加几条数据. 3.创建动态Web项目(推荐使用Maven可以用配置来 ...
- python之正则表达式及RE模块
正则表达式(匹配字符串)web界面正则匹配工具:http://tool.chinaz.com/regex/ 元字符 1 . 匹配除换行符之外的任意字符 2 \w 匹配数字字母下划线 3 \d 匹配数字 ...
- Mybatis PageHelper 简单使用
流程 1,maven 依赖 2,在 mybatis 配置文件启用插件 3,修改 service 层 依赖 <!-- https://mvnrepository.com/artifact/com. ...
- 自定义控件详解(五):onMeasure()、onLayout()
前言: 自定义控件的三大方法: 测量: onMeasure(): 测量自己的大小,为正式布局提供建议 布局: onLayout(): 使用layout()函数对所有子控件布局 绘制: onDraw() ...