JdbcTemplate类图

从类继承关系上来看,JdbcTemplate继承了基类JdbcAccessor和接口类JdbcOperation,在基类JdbcAccessor的设计中,对DataSource数据源的管理和配置,在JdbcOperation接口中,定义了通过JDBC操作数据库的基本操作方法,而JdbcTemplate提供了这些接口的实现,例如 execute(), query() , update()等方法。

回顾JDBC的简单使用

@Test
public void testJDBC(){
String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "123456";
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();
//3.执行SQL,获取结果
ResultSet rs = stmt.executeQuery("select * from user");
while(rs.next()){
System.out.println("username="+rs.getStriing("username")+" age="+rs.getInt("age"));
}
//关闭资源
rs.close();
stmt.close();
conn.close();
}

JdbcTemplate源码分析

JdbcTemplate的execute实现

execute有很多重载方法,看void execute(final String sql)方法

//执行SQL语句
@Override
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());
}

  

  //静态处理SQL
@Override
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
//获取数据库连接
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
//创建Statement
stmt = conToUse.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
//回调函数
T result = action.doInStatement(stmtToUse);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
//释放数据库连接,同时抛出一个Spring转换过的Spring数据库异常
// 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());
}
}

execute的流程图如下:

JdbcTemplate的query实现

query也有很多重载方法,void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch)

@Override
public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {
query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));
}
@Override
public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
//创建Statement
return query(new SimplePreparedStatementCreator(sql), pss, rse);
}
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"); return execute(psc, new PreparedStatementCallback<T>() {
@Override
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
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();
}
}
}
});
}

主要来看一下RowCallbackHandlerResultSetExtractor类

private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {

		private final RowCallbackHandler rch;

		public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
this.rch = rch;
} @Override
public Object extractData(ResultSet rs) throws SQLException {
//循环获取每一行数据
while (rs.next()) {
this.rch.processRow(rs);
}
return null;
}
}

通过processRow获取每行中的每一列的数据

@Override
public final void processRow(ResultSet rs) throws SQLException {
if (this.rowCount == 0) {
ResultSetMetaData rsmd = rs.getMetaData();
this.columnCount = rsmd.getColumnCount();
this.columnTypes = new int[this.columnCount];
this.columnNames = new String[this.columnCount];
for (int i = 0; i < this.columnCount; i++) {
this.columnTypes[i] = rsmd.getColumnType(i + 1);
this.columnNames[i] = JdbcUtils.lookupColumnName(rsmd, i + 1);
}
// could also get column names
}
//由子类来实现
processRow(rs, this.rowCount++);
}

  流程图如下:

DataSourceUtils对数据库连接的管理

获取连接

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
}

doGetConnection方法

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
/*把数据库的Connection放到事务管理中进行管理,这里采用是TransactionSynchronizationManager
中的ThreadLocal变量和线程绑定数据库连接
*/
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.
//如果从holder中没有获取连接,那就从数据源中获取连接
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;
}

继续来看一下TransactionSynchronizationManager是如何返回ConnectionHolder,最终的方法是在

private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = (Map)resources.get();
if(map == null) {
return null;
} else {
Object value = map.get(actualKey);
if(value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
map.remove(actualKey);
if(map.isEmpty()) {
resources.remove();
} value = null;
} return value;
}
}

是从resource中获取一个Map,这个Map中的key就是dataSource,value为ConnectionHolder,而Connection就是包装在里面,resource又是什么类型的?

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");

从上面可以了解到事务是和Connection绑定在一起的,同时使用了LocalThread来保存,使得线程安全。

最后问dataSource(数据源)又是从哪里来的?dataSource作为JdbcTemplate的基类JdbcAccessor的属性是通过Ioc容器注入,可以看一下项目数据源都需要在spring.xml或配置类中进行配置,由spring容器来管理。对于dataSource也可以使用连接池,这里就需要采用第三方dbcp或者c3p0来完成,然后又容器将dataSource交给JdbcTemplate使用。

Spring源码解析-JdbcTemplate的更多相关文章

  1. Spring源码解析系列汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...

  2. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegistry和SingletonBeanRegistry接口. 这边主要提供了 ...

  3. spring 源码解析

    1. [文件] spring源码.txt ~ 15B     下载(167) ? 1 springн┤┬вио╬Ш: 2. [文件] spring源码分析之AOP.txt ~ 15KB     下载( ...

  4. Spring源码解析——循环依赖的解决方案

    一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...

  5. Spring源码解析-ioc容器的设计

    Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...

  6. Spring源码解析之PropertyPlaceholderHelper(占位符解析器)

    Spring源码解析之PropertyPlaceholderHelper(占位符解析器) https://blog.csdn.net/weixin_39471249/article/details/7 ...

  7. Spring源码解析之BeanFactoryPostProcessor(三)

    在上一章中笔者介绍了refresh()的<1>处是如何获取beanFactory对象,下面我们要来学习refresh()方法的<2>处是如何调用invokeBeanFactor ...

  8. Spring源码解析之ConfigurationClassPostProcessor(二)

    上一个章节,笔者向大家介绍了spring是如何来过滤配置类的,下面我们来看看在过滤出配置类后,spring是如何来解析配置类的.首先过滤出来的配置类会存放在configCandidates列表, 在代 ...

  9. Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean

    Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean 七千字长文深刻解读,Spirng中是如何初始化单例bean的,和面试中最常问的Sprin ...

随机推荐

  1. LeetCode 142——环形链表 II

    1. 题目 2. 解答 2.1 方法 1 定义快慢两个指针,慢指针每次前进一步,快指针每次前进两步,若链表有环,则快慢指针一定会相遇. 当快慢指针相遇时,我们让慢指针指向头节点,快指针不变,然后每次快 ...

  2. codeforces 228E The Road to Berland is Paved With Good Intentions(2-SAT)

    Berland has n cities, some of them are connected by bidirectional roads. For each road we know wheth ...

  3. Centos7 下nginx nginx-1.13.4 安装

    环境:CentOS Linux release 7.3.1611 (Core)  Linux localhost.localdomain 3.10.0-514.26.2.el7.x86_64 #1 S ...

  4. 《剑指Offer》题十一~题二十

    十一.旋转数组的最小数字 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组{3, 4, 5, 1, 2}为{ ...

  5. wf效能分析

    听从了老师的建议我请教了其他的同学,修改了代码实现了功能四,以下是我的效能测试: 1.采用ptime.exe测试的3次截图 可以看到的是三次执行时间分别为:1.449秒:0.915秒:0.871秒,取 ...

  6. 20145214 《Java程序设计》第9周学习总结

    20145214 <Java程序设计>第9周学习总结 教材学习内容总结 JDBC简介 JDBC全名Java DataBase Connectivity,是java联机数据库的标准规范.它定 ...

  7. iOS- xib(nib) 的重用(在有些情况下有利于加快项目功能的实现)

    0.前言 在项目开发中,我们经常会碰到,某些视图View 内部基本空间都一样,只是数据不同,这时,我们可以用xib来将这个视图封装起来多次重用, (例如,大小固定 控件固定的TableViewCell ...

  8. 转 【.NET平台下使用MongoDB入门教程】

    目录 一.了解MongoDB 二.MongoDB特点 三.安装及常用命令 3.1 下载安装 3.2 启动服务器 3.3 常用操作 3.4 其他命令 3.5 做成windows服务 四.批处理程序开启M ...

  9. 会话模型与SSO

    关于会话模型其实网站已有很多帖子说明,其中有关于sessionid,cookie以及他们之间的关系,自己先了解吧 1 会话模型 会话模型是客户端和服务端交互的一种模型,会话模型友好的处理了客户端有无通 ...

  10. xpath教程一---简单的标签搜索

    工具 Python3版本 lxml库[优点是解析快] HTML代码块[从网络中获取或者自己杜撰一个] requests[推荐安装,从网页上获取网页代码练手,再好不过了] 讲解 网页代码都是成对的标签, ...