前言:上一篇博客介绍了模板方法模式,并且给出了一个小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. spring cloud-zuul的Filter详解

    在前面我们使用zuul搭建了网关http://blog.csdn.net/liuchuanhong1/article/details/59056278 关于网关的作用,这里就不再次赘述了,我们今天的重 ...

  2. iOS钉钉远程打卡助手(支持越狱和非越狱)

    前言:本文主要讲述使用hook方式实现钉钉远程打卡功能,涉及到tweak相关知识,如果你不想了解具体实现细节可直接到我的Github地址参考安装(包含越狱和非越狱两种方法)   你是不是像小编一样每个 ...

  3. 一天浓缩学习webpack经过

    熟话说浓缩就是精华,哈哈,所以就这么简单粗暴的介绍下吧,写下的都是精华. 已经不是第一次听说webpack,但是我的起步有点晚,现在才看.开门见山~~ 1.什么是webpack? webpack是当下 ...

  4. HTTP常用方法

    GET : 获取资源 get方法用来请求访问已被URI识别的资源. 请求 GET /index.html HTTP/1.1 HOST:www.baidu.com 响应 返回index.html的页面资 ...

  5. Python起步

    最近研究系统自动化测试想起了一年前有学习Python的想法,借此机会准备抽时间好好学学.为方便以后学习和查询特写以下博客! Python基础 1. Python数据结构 (1)Python字符串 (2 ...

  6. The POM for * is invalid

    The POM for yanan:jar:1.0-SNAPSHOT is invalid, transitive dependencies (if any) will not be availabl ...

  7. linux中操作java进程

    通过 ps -ef|grep java 来得到真正运行的线程 通过kill -9 XXXXX来杀死正在运行的线程,其中XXXXX是上面java线程的序号. 另外还有2个指令我也不熟悉,一个是cat,一 ...

  8. Windows 产品激活状态、密钥等信息查看

    目前,大多PC都是预装了微软家的桌面级系统 Windows ,这也算是微软一大得意之作.可是 Windows 产品可不是免费的,是要 $ 的.可能在中国,大多数系统可能是盗版过来的,像当年的雨林木风( ...

  9. IO (一)

    1 IO(Input Output)流概述 IO流用来处理设备之间的数据传输. java对数据的操作是通过流的方式. java用于操作流的对象都在IO包中. 流按操作数据分为两种:字节流和字符流. 流 ...

  10. curl错误码说明

    1.得到错误码 $errno=curl_errno($ch); if($errno!=0){ -- } 2.错误码说明 <?php return [ '1'=>'CURLE_UNSUPPO ...