JDBCTemplate与模板设计方法(二)

前言:上一篇博客介绍了模板方法模式,并且给出了一个小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采用模板设计模式对其进行了高度封装的过程,并且分析它的源码,和使用过程,源码只要理解了就行, 用的时候可以直接调用,其中它本身大量使用了回调机制,在方法内部声明一个类实现回调接口,调用接口中的方法进行处理。主要的重要部分是源码的剖析,理解它的回调接口,然后理解模板设计方法带给我们的好处,在项目中多多使用它,毕竟封装可是面向对象的三大特性之一。

转载于:https://www.cnblogs.com/wyq178/p/7518339.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值