前言:上一篇博客介绍了模板方法模式,并且给出了一个小demo,简单对模板方法进行了实现,接下来我们把目光转向spring的源码JDBCTemplate,看一看spring是如何对jdbc进行高度封装的。

本篇博客的目录:

一:传统的jdbc存在的缺陷和不足

二:JDBCTemplate的实现原理

三:JDBCTemplate的使用方法

五:总结

正文:

一:传统的jdbc存在的缺陷和不足

1.1首先我们来一下以往我们使用jdbc进行数据库操作的代码是什么样的?

public class UpdateDateBase {

   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/jdbc_db";
static final String USER = "root";
static final String PASS = "123456"; public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{ Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
stmt = conn.createStatement();
String sql = "UPDATE student " +"SET age = 22 WHERE id in (100, 101)";
stmt.executeUpdate(sql);
sql = "SELECT id, first, last, age FROM student";
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
int id = rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
}
rs.close();
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
conn.close();
}catch(SQLException se){
}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
}
}

这里只是做了一个简单的操作,对数据进行更新,再查询显示出来,可以看到这么一大长串,假如我们再进行一个删除操作,还得把这些步骤再来一遍,天呐,头都要大了,写代码最烦的就是写重复烦躁的代码。这就是传统jdbc的编码,存在以下不足

1:对外暴露的细节太多,比如把数据库名、密码、用户名、Ip等都暴露出来,万一代码落入到还有不轨企图的人手里,只要看一下你的jdbc,它就可以得到你数据库的所有信息。其实安全这一点对企业来说非常重要,如果没有参加工作的童鞋可能还意识不到这一点

2:冗余、重复的代码太多,都是做一些重复机械的操作,首先是获取驱动、得到连接、生成Statement,再编译sql,执行,取结果遍历,最后还得再关闭结果集、关闭连接、关闭statement,防止抛异常,还得考虑事务,万一出错还得考虑回滚等等。

3:代码编写比较繁复,这其中万一有一个步骤出错了,比如忘记关闭数据库连接池了,这样就会发生很多莫名奇妙的问题,比如再次更新数据库发现没有变化,这就是没有关闭连接池导致的。这种细节性操作很容易酿造大的事情,程序员需要关注的细节过多

4: 向方法中的sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应,这其中万一出错了,就会导致无法操作数据库

二:JDBCTemplate源码分析

2.1:关于JDBCTemplate:它是这样一个类,它把我们对数据库的操作jdbc代码高度封装,并且采用了模板方法的设计思想,将其抽象成回调接口和工具类调用的方法,把特定的步骤通过工具类来组装,这样可以实现固定步骤的高度可重用。只需要编写一次,我们就可以进行重复利用起来。

2.2:JDBCTemplate的继承关系

上面是它的继承关闭图,它继承自抽象类JdbcAccessor和JdbcOperations接口。其中JdbcAccessor设定了处理数据源DataSoucre和sql的异常检验操作,而JdbcOperations则负责具体的查询query、update操作、批处理batchUpdate等,同时继承自两个父类,让JDBCTemplate具有操作数据库和处理数据源、获取异常的能力。

2.3:JDBCTemplate的成员变量

这些成员变量是它本身的,我来翻译一下它的含义:

private static final String RETURN_RESULT_SET_PREFIX = "#result-set-";//前缀
private static final String RETURN_UPDATE_COUNT_PREFIX = "#update-count-";//计数前缀
private NativeJdbcExtractor nativeJdbcExtractor;//本地JDBC执行器
private boolean ignoreWarnings = true;//是否忽略警告
private int fetchSize = 0;//查询到的大小
private int maxRows = 0;//最大行数
private int queryTimeout = 0;//查询时间
private boolean skipResultsProcessing = false;//是否跳过结果处理
private boolean skipUndeclaredResults = false;//是否跳过非公共结果
private boolean resultsMapCaseInsensitive = false;//Map结果的是否大小写敏感

但是因为它继承了一个抽象类和接口,它同样享有JdbcAccesor的成员变量,可以看到DataSoucre已经注入了,但它是private的,虽然可以继承,但是不能使用,不过可以引用JdbcAccesor中的setter方法进行设值

父类中的这些很简单,基本上可以很直接的看出来,它们分别是日志记录器、数据源、sql异常转换器、是否懒加载,这些成员变量是全局的,也就是说它们存在着线程安全的问题,为此Spring还专门采用了单例加锁的设计模式来获取exceptionTranslator对象,以下是源码:

可以看到它使用了synchronize关键字,这个锁是方法级别上的,获取exceptionTranslator对象,根据dataSoure是否为空来判断实现的不同子类SQLErrorCodeSQLExceptionTranslator(需要DataSource)和SQLStateSQLExceptionTranslator(不需要DataSource);

2.4:jdbcTemplate的构造方法

可以看到它有三个构造方法,标记为①的是个空构造方法,这个不用多说了。第二个它的话它引用了JdbcAccesor注入了DataSource,然后调用了父类中的属性设置后的方法this.arterPropertiesSet(),我们来看看它的源码:

可以看到这是和懒加载有关的一个方法,没有懒加载,就会调用本类中的方法调用2.3中讲的方法获取异常转换器中

第三个构造方法,它在第二个的基础上增加了设置LazyInit的字段,设置它是否支持懒加载,关于懒加载:它是一种节省资源提高效率的做法,数据只有在真正用到的时候才去加载它。

2.5:jdbcTemplate执行sql原理

我们来看一下它执行sql的方法exexute的源码:

首先它是一个日志记录,级别在debug,然后方法中声明了一个类,这个类继承了StatementCallback接口和sqlProvider接口,这个接口只有一个方法,主要负责的就是用Statement这个对象执行sql。

最后再把这个接口传入execute重载方法,我们来看一下它对接口作了哪些处理:

我们来分析一下这段源码,它主要是获取connection对象,再通过对象赋值,通过本地jdbc提取器再次获取,然后再通过该对象创建Statement,再进行设值,再通过对象赋值给另一个对象,至于Spring中为什么经常出现这种代码-指的是对象赋值转接,这里可以理解为对象的一个复制过程,然后再做重新的处理。再调用接口中的doInstatement方法执行sql获取结果,又再次出现把对象转接过去,对结果进行赋值,然后在catch代码块中关闭Statement和释放连接,再抛出异常,在finally块中确保关闭Statement和Connection.

2.6:jdbcTemplate的update方法源码分析

我们看到这就是它的update方法,首先是一个断言机制:sql必须不为null,关于断言就是一种假设机制,假设程序运行到这里对参数进行校验,如果不满足的话,将会抛出AssertionError异常。然后是日志记录sql,接着是一个方法内部类,这里又用到实现了回调接口StatementCallback,它的主要方法是doInstatement,调用jdbc中的的executeUpdate方法执行完返回一个int类型的值,最后返回这个值,然后再次调用execute方法,把方法内部的实现类传进去,和刚才的一样,做了必要的关闭Statement和connection操作,还有一些异常的抛出操作和警告等的处理

2.7:jdbcTemplate的批量更新方法源码

批量更新batchupdate可以看到它传入的是一个sql的数组,那么我们在调用的时候需要传入肯定是一个sql的数组集合。然后和上面的一样断言、日志开启,再在方法内部新建一个类实现Statement回调接口,再其中先判断是否支持批量查询,再进行循环遍历,通过appendSql方法拼接sql语句,再用statement批量更新,最后返回影响的行数。

上面的源码是进行一场判断的处理,抛出具体哪个sql异常,else的部分是指不支持批量查询的情况,它就会抛出异常Invaild batch sql Statement+具体的sql语句,最后再返回影响的行数

三:jdbcTemplate的使用方法

3.1:首先在application.xml中配置jdbcTemplate Bean:

这里可以看到配置的过程中引用了DataSource数据源,这里的主要原因是我在2.3中讲到的它有DataSource这个成员变量,然后我们来看一下DataSource配置了哪些东西

这里的DateSource主要是数据库的配置,数据库也就是我们数据的来源,至于其中的${jdbc.property}指的是读取一个配置文件的值,一个.properties文件结束,关于如何读取文件,我的博客之前也有讲过:点我到达

不过使用这种方式的话,记得要在Spring的配置文件中写上,location是资源文件的路径:

3.2:如果其他配置好的话,接下来我们看看如何具体使用它:

jdbcTemplate肯定是在Dao层了,我们用@AutoWired注入这个对象,然后可以调用它的方法执行sql就行了。其他的比如关闭操作、异常处理、回滚等操作压根不用操心,这个类已经帮我们处理好了一切,怎么样,够简单吧。

ps:其他的使用方法同理。只需要用这个jdbctemplate调用方法就可以了。

 四:总结

本篇博客主要讲解了原始jdbc的不足和jdbctemplate采用模板设计模式对其进行了高度封装的过程,并且分析它的源码,和使用过程,源码只要理解了就行, 用的时候可以直接调用,其中它本身大量使用了回调机制,在方法内部声明一个类实现回调接口,调用接口中的方法进行处理。主要的重要部分是源码的剖析,理解它的回调接口,然后理解模板设计方法带给我们的好处,在项目中多多使用它,毕竟封装可是面向对象的三大特性之一。

JDBCTemplate与模板设计方法(二)的更多相关文章

  1. IP地址的规划和设计方法(二)

    五,IP地址规划方法           (1)IP地址规划的基本步骤           网络地址规划须要按下面6步进行:           a)推断用户对网络与主机数的需求:           ...

  2. Axure中移动端原型设计方法(附IPhoneX和IPhone8最新模板)

    Axure中移动端原型设计方法(附IPhoneX和IPhone8最新模板) 2018年4月16日luodonggan Axure中基于设备模板的移动端原型设计方法(附IPhoneX和IPhone8最新 ...

  3. thinkphp二维数组模板输出方法

    thinkphp二维数组模板输出方法 先写个记录,有空再整理发上来

  4. Robot Framework测试框架用例脚本设计方法

    Robot Framework介绍 Robot Framework是一个通用的关键字驱动自动化测试框架.测试用例以HTML,纯文本或TSV(制表符分隔的一系列值)文件存储.通过测试库中实现的关键字驱动 ...

  5. paip.自适应网页设计 跟 响应式 设计方法与工具补充(2)o54

    paip.自适应网页设计 跟 响应式 设计方法与工具补充(2)o54 #-----响应式 设计框架 Bootstrap比较热门. Foundation 号称是世界上最先进的响应式前端框架. #---绝 ...

  6. 黑盒测试用例设计方法&理论联系实际-> 功能图法

    一. 概念 功能图由状态迁移图和布尔函数组成.状态迁移图用状态和迁移来描述.一个状态指出数据输入的位置(或时间),而迁移则指明状态的改变.同时要依靠判定表或因果图表示的逻辑功能.例,一个简化的自动出纳 ...

  7. [liu yanling]黑盒测试用例设计方法

    1. 概述 黑盒测试用例设计方法包括等价类划分法.边界值分析法.错误推测法.因果图法.判定表驱动法.正交试验设计法.功能图法等. 2. 等价类划分法 2.1.          概念 等价类划分法是把 ...

  8. 在verilog中关于inout口的设计方法

    在学习IIC的时候我们知道这么设计inout inout   scl : reg    scl_reg ,  scl_en ; scl  = scl_en ?   scl_reg : 1'dz ; 当 ...

  9. IP地址规划和设计方法

    IP地址规划和设计方法 无类域间路由技术需要在提高 IP 地址利用率和减少主干路由器负荷两个方面取得平衡 网络地址转换 NAT 最主要的应用是专用网,虚拟专用网,以及 ISP 为拨号用户 提供的服务 ...

随机推荐

  1. nodeJS里面的模块

    this 打开cmd,执行如下命令 nodeconsole.log(this); 输出如上信息,表示this是global,每个电脑的配置信息不一样的话,可能会有所差别的. 然后新建一个文件,写下如下 ...

  2. APACHE 服务器开启URL REWRITE模块的方法

    最近做wordpress,发现固定链接总是设置不了.后来发现是由于apache服务器的URL REWIRITE模块没有开启导致. 查询了资料之后终于设置成功,记录下步骤: 1:开启apache的url ...

  3. 解决:mysql is blocked because of many connection errors;

    标签:because service foreign errors closed 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:// ...

  4. ThinkPHP5上传图片并压缩为缩略图

    使用thinkphp开发app后端中,需要实现一个处理上传图片队列的功能 这是个上传多图片保存并且需要对其中一张图片进行压缩的功能 (使用的html5 mui框架开发app,如果直接载入原图,app客 ...

  5. 语句、变量等js最基本知识

    JavaScript的最为基本知识 1语法 js是区分大小写的:标识符就是指变量.函数.属性的名字或者是参数,标识符可以是字母,下划线,美元符号,数字,注意第一个不能是数字:js采用的是驼峰大小格式: ...

  6. asp.net -mvc框架复习(7)-基于MVC搭建用户登录项目框架

    整体框架: 一.搭建Model层 1.添加通用数据访问类 2.添加实体类(封装和传递数据,和数据库中数据表对应) 3.添加数据访问类(通常和实体类同名,但是后缀名发生改变) 二.搭建控制器层Contr ...

  7. ffmpeg批量实现视频转码命令行

    ffmpeg实现视频转码命令行,result需要提前建好作为保存转码后的视频路径: ffmpeg -i .mp4 -vcodec h264 "result\1.mp4" 当有大量视 ...

  8. C语言知识点总结

    本文采用思维导图的方式撰写,更好的表述了各知识点之间的关系,方便大家理解和记忆.这个总结尚未包含C语言数据结构与算法部分,后续会陆续更新出来,文中有漏掉的知识点,还请大家多多指正.

  9. arduino扩展IO与M74HC595B芯片的使用,挪车电话提示牌的设计

    2018-01-0915:39:24 视频连接 首先arduino中shiftOUT()函数的定义与说明! shiftOut()描述将一个数据的一个字节一位一位的移出.从最高有效位(最左边)或最低有效 ...

  10. .net Core连接MongoDB

    前两天在学习MongoDB相关的知识,做了个小Demo,大概是省份里面有多少所学校 连接MongoDB首先要通过Nuget添加一个MongoDB的包,下载此包 安装完毕后开始写代码了,创建一个省份实体 ...