当前位置: 首页 > news >正文

模板方法模式在JDBCTemplate中的应用

上一篇中系统总结了模板模式的原理和使用,提到了模板方法和回调接口。回调接口和模板方法类之间的关系可以看作服务与被服务的关系,模板方法类想要回调接口做事,就要提供相应的资源,接口用提供的资源做事,完事后,模板方法类来处理公开的资源,回调接口不需要在关心这些。今天再记录一下JDBCTemplate中模板方法模式的应用。

这里先给出JDBC的初级代码:

public class JDBCTemplate<T> {/*** inset updata delect操作时调用此方法*/public void update(String sql,Object... params){Connection con = null;PreparedStatement ps = null;int count = 0;try {con = JDBCUtil.getConnection();ps = con.prepareStatement(sql);if (params != null) {for (int i = 0; i < params.length; i++) {ps.setObject(i+1, params[i]);}}count = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {//connection不能关闭,因为还要在Service中使用
//       JDBCUtil.close(con, ps);JDBCUtil.close(null, ps);}}
    /*** 执行查询时用的方法* @param sql* @param rm* @param params* @return*/public  List<T> query(String sql,RowMapper rm,Object... params){Connection con = null;PreparedStatement ps = null;ResultSet rs = null;List result = new ArrayList<>();try {con = JDBCUtil.getConnection();ps = con.prepareStatement(sql);if (params != null) {for (int i = 0; i < params.length; i++) {ps.setObject(i+1, params[i]);}}rs = ps.executeQuery();while(rs.next()){Object obj = rm.mapRow(rs);result.add(obj);}} catch (Exception e) {e.printStackTrace();} finally {JDBCUtil.close(null, ps, rs);}return result;}
    public Object query4Object(String sql,RowMapper rm,Object... params){List data = query(sql, rm, params);return data.isEmpty() ? null :data.get(0);}}

上面的代码把对数据库的访问操作封装成了两个方法,对于update(String sql,Object... params)方法,在实际的开发中,新增数据有时需要记录新增数据的主键,在JDBC中提供了这样一个方法:

con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

因此,con.prepareStatement(sql)的参数如果写死是不能满足需求的。基于模板模式的思路,定义专门创建PreparedStatement对象的接口:

public interface PreparedStatementCreater {/*** 创建PreparedStatement对象* @param con* @return*/public PreparedStatement createPreparedStatement(Connection con,KeyHolder keyHolder) throws Exception;
}

然后prepareStatement的创建和sql传参和赋值全部交给调用者实现,我只负责运行

    /*** 当保存需要返回主键的时候调用此方法* @param psc 用于创建PreparedStatement对象* @param keyHolder 用于存放主键*/public void update(PreparedStatementCreater psc,KeyHolder keyHolder){Connection con = null;PreparedStatement ps = null;ResultSet rs = null;int count = 0;try {con = JDBCUtil.getConnection();
//       ps = con.prepareStatement(sql);
//
//       if (params != null) {
//          for (int i = 0; i < params.length; i++) {
//             ps.setObject(i+1, params[i]);
//          }
//       }//prepareStatement交给调用者创建,我只负责运行,其他的不再关心ps = psc.createPreparedStatement(con, keyHolder);ps.executeUpdate();rs = ps.getGeneratedKeys();List keys = new ArrayList();//保存返回主键的代码ResultSetMetaData rsmd = rs.getMetaData();int columnCount = rsmd.getColumnCount();if (rs.next()){for (int i=0;i<columnCount;i++){keys.add(rs.getObject(i+1));}}//返回值是void,用keyHolder返回主键,调用者传空的keyHolder进来keyHolder.setList(keys);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtil.close(null, ps , rs);}}
public class KeyHolder {private List list;public KeyHolder() {list = new ArrayList();}public KeyHolder(List list) {this.list = list;}public void setList(List list) {this.list = list;}/**联合主键*/public List getKeyList() {return list;}/**单个主键*/public Object getKey(){if(list.isEmpty()){return null;}return list.get(0);}
}

此时当调用者调用update方法时,迫使实现PreparedStatementCreater 接口中的createPreparedStatement方法。

对于public List<T> query(String sql,RowMapper rm,Object... params)方法,如果我要查询一个部门对象,并把部门对应的员工映射进来时,根据查询结果逐条映射的逻辑是不对的

while(rs.next())
{Object obj = rm.mapRow(rs);result.add(obj);
}
select * from dept d inner join emp e on e.dept_id=d.id 

此时多条数据映射一个部门对象,根据查询结果集逐条映射的逻辑是不对的,因此查询方法也要进行改造,同样是通过的传入接口迫使调用者实现剩余逻辑的方法来做

    /*** 执行查询时用的方法* 当进行一方查询多方数据时,使用此方法  一个部门对应多个员工 dept List<Emp>* @param sql* @param params* @return*/public Object query(String sql,ResultSetExtractor rse, Object... params){Connection con = null;PreparedStatement ps = null;ResultSet rs = null;Object obj = new Object();try {con = JDBCUtil.getConnection();ps = con.prepareStatement(sql);if (params != null) {for (int i = 0; i < params.length; i++) {ps.setObject(i+1, params[i]);}}rs = ps.executeQuery();obj = rse.extractData(rs);} catch (Exception e) {e.printStackTrace();} finally {JDBCUtil.close(null, ps, rs);}return obj;}

以上就是JDBC封装中对模板方法的使用。

在spring中,正是根据这种思想实现各种JDBCTemplate中的各种模板方法,通过相应回调接口所公开的API自由度的大小,简单分为4组:

面向Connection的模板方法。通过ConnectionCallback接口公开java.sql.Connection进行数据访问,不需要关心连接的释放和获取,但自由度很大,一般避免直接使用面向Connection接口的模板方法进行数据访问。

面向Statement的模板方法。主要处理基于静态sql的数据访问请求。通过StatementCallback接口对外公开java.sql.Statement,缩小了回调接口内的权限范围,相比上一种提高了安全性。

面向PreparedStatement的模板方法。通过PreparedStatementCreater接口公开Connection允许PreparedStatement创建(创建需要传入包含参数的SQL),PreparedStatement创建之后,公开给PreparedStatementCallback回调接口。

面向CallableStatement的模板方法。通过CallableStatementCreater接口公开Connection以便创建用于调用存储过程的CallableStatement,再通过CallableStatementCallback公开创建的CallableStatement实现基于存储过程的数据访问。

http://www.lryc.cn/news/139740.html

相关文章:

  • 如何在Debian中同步系统时间?Debian 系统时间配置(NTP服务)
  • 模板方法模式(十六)
  • 机器学习笔记之优化算法(十九)牛顿法与正则化
  • Java面试之单例模式的六种实现方式
  • re正则入门
  • C++ Day5
  • el-element:自定义参数
  • “分布式”与“集群”初学者的技术总结
  • C++day5(静态成员、类的继承、多继承)
  • 2023MySQL+MyBatis知识点整理
  • 【随笔】如何使用阿里云的OSS保存基础的服务器环境
  • 汽车电子笔记之:AUTOSA架构下的多核OS操作系统
  • 解决华为云ping不通的问题
  • 数据结构入门 — 链表详解_双向链表
  • 时序预测 | MATLAB实现PSO-KELM粒子群算法优化核极限学习机时间序列预测(含KELM、ELM等对比)
  • SSL/TLS协议的概念、工作原理、作用以及注意事项
  • [Stable Diffusion教程] 第一课 原理解析+配置需求+应用安装+基本步骤
  • uniapp结合Canvas+renderjs根据经纬度绘制轨迹(二)
  • VR全景加盟会遇到哪些问题?全景平台会提供什么?
  • 如何进行微服务的集成测试
  • spark grpc 在master运行报错 exitcode13 User did not initialize spark context
  • nginx 反向代理的原理
  • 【SpringBoot】第二篇:RocketMq使用
  • 飞天使-vim简单使用技巧
  • 分布式搜索引擎----elasticsearch
  • AnnotationConfigApplicationContext类和ClasspathXmlApplicationContext类的区别?
  • 使用VSCode SSH实现公网远程连接本地服务器开发的详细教程
  • Codeforces Round 894 (Div. 3)
  • ACL2023 Prompt 相关文章速通 Part 1
  • “R语言+遥感“水环境综合评价方法