一、背景

Java开发人员现在对数据库的操作一般会用到诸如像Hibernate,Mybatis,SpringJdbcTemplate等ORM组件,但是这些组件是怎样从原始的编码方式一步步封装过来的呢 ?

二、最原始的编码方式

如下面代码所示: 在笔者05年刚毕业的时候,曾经是这样写Jdbc访问数据库的.

/**

*

* 类描述: 用来测试jdbc连接的类(oracle,mysql)

* 创建人:keven

* @version V1.0

*/

publicclass DBConnectionTest{

/**

* 一个数据库的连接我们要具备三项条件 ,一个是数据库地址,

* 一个是访问数据库的用户名和密码

* 一个是数据库的类型

* @Title: testDBConnection

* @Description: TODO

*/

publicstaticvoid testDBConnection(){

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try {

//加载MySQL的驱动程序

Class.forName("com.mysql.jdbc.Driver");

//连接数据库的路径(关键字+IP地址+数据库名称)

String url = "jdbc:mysql://127.0.0.1/open";

//数据库的用户名

String user = "root";

//数据库密码

String password = "123456";

System.out.println("现在开始连接MYSQL数据库");

//获取连接

con = DriverManager.getConnection(url,user,password);

System.out.println("连接MYSQL数据库成功");

String sql = "SELECT * FROM USER ";

//实例化预编译语句对象

pre= con.prepareStatement(sql);

//pre.setInt(1, deptId);

//返回结果集

result = pre.executeQuery();

//将结果集放到我们的java对象

while(result.next()){

System.out.println(result.getObject(1)+"----"+result.getObject(2)

+"----"+result.getObject(3)+"----"+result.getObject(4)+"----");

}

} catch (Exception e) {

e.printStackTrace();

}finally{

try {

if(result!=null){

result.close();

}

if(pre!=null){

pre.close();

}

if(con !=null){

con.close();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

publicstaticvoid main(String[] args) {

DBConnectionTest.testDBConnection();

}

}

三、封装的过程和思路

总得来说,Java组件封装的原则就是高内聚,低耦合,直白一点的解释就是 将重复性的代码提取出去作为工具类,尽量减少类与类之间的固定依赖.

1) DbUtil工具类

通过查看最原始编码方式的代码 ,我们可以看出,获取数据库的连接和关闭数据库连接的代码,在每一次操作中都需要,所以我们可以思考一下,将这部分代码提取出去.

* 新建DbUtil工具类 ,用于数据库的开连接和关连接

/**

* 类描述:封装第一步 我们把取得连接和关闭连接抽取出来成为独立的方法放入工具类里面

* 第二步:我们是否要考虑将我们的数据库连接的四要素(类型,用户名 密码 ,地址 抽取出来做成配置文件的方式)

* 创建人:keven

* @version V1.0

*/

publicclass DbUtil {

publicstatic Connection getConnection() throws Exception {

Connection con;

//加载MySQL的驱动程序

Class.forName("com.mysql.jdbc.Driver");

//连接数据库的路径(关键字+IP地址+数据库名称)

String url = "jdbc:mysql://127.0.0.1/open";

//数据库的用户名

String user = "root";

//数据库密码

String password = "123456";

System.out.println("现在开始连接MYSQL数据库");

//获取连接

con = DriverManager.getConnection(url,user,password);

returncon;

}

/**

* 关闭数据库连接

* @Title: closeConnection

* @Description: TODO

* @param con

* @param pre

* @param result

*/

publicstaticvoid closeConnection(Connection con, PreparedStatement pre,

ResultSet result) {

try {

if(result!=null){

result.close();

}

if(pre!=null){

pre.close();

}

if(con !=null){

con.close();

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

通过工具类的封装,我们可以继续在工具类里面将数据库的信息通过配置文件加载,以及启用流行的连接池技术,在这里不在赘述.

2) 增删改方法的封装

在封装了DbUtil工具类的基础上,我们试着做一个单表的增删改查,请看以下代码:

/**

* 关于用户表的增删改查方法

* @authorkeven

*/

publicclass UserDaoTest{

/**

* user表的增加方法

*/

publicint addUser(User user){

introws = 0;

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

String addSql = " INSERT INTO USER(NAME,AGE,SEX)VALUES(?,?,?)";

pre = con.prepareStatement(addSql);

pre.setString(1, user.getName());

pre.setInt(2, user.getAge());

pre.setString(3, user.getSex());

rows = pre.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnrows;

}

/**

* user表的删除方法

*/

publicint delUser(intuserId){

introws = 0;

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

String deleteSql = "DELETE FROM USER WHERE ID = ?";

pre = con.prepareStatement(deleteSql);

pre.setInt(1, userId);

rows = pre.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnrows;

}

/**

* user表的更新方法

*/

publicint updateEmp(User user){

introws = 0;

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

String updateSql = " UPDATE USER SET NAME =?, AGE=?,SEX=? WHERE ID =? ";

pre = con.prepareStatement(updateSql);

pre.setString(1, user.getName());

pre.setInt(2, user.getAge());

pre.setString(3, user.getSex());

pre.setInt(4, user.getId());

rows = pre.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnrows;

}

/**

* user表的查询方法

*/

public List<User> getUserList(){

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

List<User> userList = new ArrayList<User>();

try {

con = DbUtil.getConnection();

String sql = "SELECT ID,NAME,AGE,SEX FROM USER ";

//实例化预编译语句对象

pre= con.prepareStatement(sql);

//pre.setInt(1, deptNo);

//返回结果集

result = pre.executeQuery();

//将结果集放到我们的java对象

User user;

while(result.next()){

user = new User();

user.setId(result.getInt("id"));

user.setName(result.getString("name"));

user.setAge(result.getInt("age"));

user.setSex(result.getString("sex"));

userList.add(user);

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

//逐一将上面的对象全部关闭,因为如果不关闭的话会影响数据库的性能,并且占用资源

//逐一关闭的顺序 最后使用的最先关闭

DbUtil.closeConnection(con, pre, result);

}

returnuserList;

}

}

进一步观察增删改方法 ,除了Sql语句和参数传入的不同,其他代码其实也是重复的,我们是否可以考虑将这些公用的代码也提取出去呢?

伟大的Java程序员们都是”懒鬼”,一切都是为了少些一些重复的代码以提高工作效率.

我们可以新建一个模板类JdbcTemplate, 对增删改方法进行封装,外部只需要传入sql语句和sql语句需要用到的参数.

/**

* 类描述: jdbcdao模板类

* 通过参数的动态传入来简化dao类的代码

* 1:sql

* 2:sql需要传入的变量的值

* 创建人:keven

*/

publicclass JdbcTemplate{

/**

* 封装增删改的模板方法

* @Title: updateTemplate

* @Description: TODO

* @param sql

* @param params

* @return

*/

publicint updateTemplate(String sql,Object[] params){

introws = 0;

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

pre = con.prepareStatement(sql);

//设置sql所需要的参数

if(params!=null){

for(inti=0;i<params.length;i++){

pre.setObject(i+1, params[i]);

}

}

rows = pre.executeUpdate();

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnrows;

}

}

通过上面步骤的封装,我们再来看看让对单表增删改的操作是如何方便简单的,新建一个单表增删改的测试类,继承我们封装的模板类,代码如下:

/**

* 继承了模板类的单表增删改操作

* @authorkeven

*

*/

publicclass UserDaoTemplateTest extends JdbcTemplate {

/**

* user表的增加方法

* @Title: adduser

* @Description: TODO

* @param user

* @return

*/

publicint addUser(User user){

String addSql = " INSERT INTO USER(NAME,AGE,SEX)VALUES(?,?,?)";

Object[] params = new Object[]{user.getName(),user.getAge(),user.getSex()};

returnthis.updateTemplate(addSql, params);

}

/**

* user表的删除方法

* @Title: deluser

* @Description: TODO

* @param userNo

* @return

*/

publicint deluser(intuserId){

String deleteSql = "DELETE FROM USER WHERE ID=?";

Object[] params = new Object[]{userId};

returnthis.updateTemplate(deleteSql, params);

}

/**

* user表的修改方法

* @Title: updateuser

* @Description: TODO

* @param user

* @return

*/

publicint updateUser(User user){

String updateSql = " UPDATE USER SET NAME =?, AGE=?,SEX=? WHERE ID =? ";

Object[] params = new Object[]{ user.getName(),user.getAge(),user.getSex(),user.getId()};

returnthis.updateTemplate(updateSql, params);

}

}

回过头看看我们的封装过程和代码,是不是对于开发人员来讲 ,越来越简单,代码写的越来越少,这就是Java在实际开发过程中需要用到大量前辈们封装的组件的原因.

3) 查询方法的封装

在增删改方法的封装过程当中,我们发现,增删改的操作,方法的返回值是固定的,但是查询方法的返回值是不固定的,查询不同的表,返回的是不同对象,也有可能是返回的其他类型的值.

通过以上分析,我们封装查询方法的时候,只能返回一个固定格式的对象或者列表,让执行查询的人来解析固定格式的结果得到自己想要的返回值.

两种方式:

a:返回一个List<Map<Object,Object>结构

在JdbcTemplate模板类面新加查询模板方法

/**

* 利用 Map结构来获取每行的记录 以List<Map>返回

* @Title: queryForList

* @Description: TODO

* @param sql

* @param params

* @return

*/

public List<Map<String,Object>> queryForList(String sql,Object[] params){

List<Map<String,Object>> mapList = new ArrayList<Map<String,Object>>();

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

pre = con.prepareStatement(sql);

//设置sql所需要的参数

if(params!=null){

for(inti=0;i<params.length;i++){

pre.setObject(i+1, params[i]);

}

}

result = pre.executeQuery();

//怎么讲result的结果集返回给mapList

//1:得到结果集里面每一个元数据的对象

ResultSetMetaData metaData = result.getMetaData();

//获取结果集的列数

intcolumnNum = metaData.getColumnCount();

Map<String,Object> mapObj;

while(result.next()){

mapObj = new HashMap<String,Object>();

for(inti = 0;i<columnNum;i++){

//根据索引获取列名(map的key)

String columnName = metaData.getColumnLabel(i+1);

//根据列名或者值(map的value)

Object value = result.getObject(columnName);

mapObj.put(columnName,value);

}

mapList.add(mapObj);

}

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnmapList;

}

这种封装方式在执行查询时候,获取的结果是List<Map<Object,Object>>结构的值,需要自己再进行转化,但是对于查询来说,就非常的简单了。

在UserDaoTemplateTest类里面新加查询方法

//查询方法如果要封装成模板类

//主要要去思考每一个查询的方法返回的类型是不一样的

//1:List<Map<String,Object>>

//2:我们需要再Dao层就做到返回我们需要的List<bean>

public List<Map<String,Object>> getUserListMap(){

String sql = "SELECT ID,NAME,AGE,SEX FROM USER ";

Object[] params = new Object[]{};

returnthis.queryForList(sql, params);

}

B:返回一个接口的匿名内部类

这种方式,封装起来稍微复杂一些,但是对于查询方来说,就可以直接在查询方法里面获取自己想要的对象,返回List<Bean>,

非常简单。

步骤:

* 新建一个接口 RowMapper<T>,成员是一个匿名的内部类

/**

* 通过传入ResultSet对象讲每一条记录通过泛型映射成对应的对象 使用的是接口的匿名内部类

*/

publicinterfaceRowMapper<T>{

public T mappingRow(ResultSet rs,intrownum) throws SQLException;

}

* 在JdbcTemplate模板类里面新增模板查询方法

public <T> List<T> queryForList(RowMapper<T> mapper,String sql,Object[] params){

List<T> returnResult = new ArrayList<T>();

Connection con = null; //一个数据库连接

PreparedStatement pre = null; //预编译对象

ResultSet result = null; //结果集

try{

con = DbUtil.getConnection();

pre = con.prepareStatement(sql);

//设置sql所需要的参数

if(params!=null){

for(inti=0;i<params.length;i++){

pre.setObject(i+1, params[i]);

}

}

result = pre.executeQuery();

intrownum = 0;

while(result.next()){

rownum++;

returnResult.add(mapper.mappingRow(result, rownum));

}

}catch(Exception e){

e.printStackTrace();

}finally{

DbUtil.closeConnection(con, pre, result);

}

returnreturnResult;

}

* 查询的时候,通过实现匿名的内部类来获取结果,直接映射到Java对象当中 ,如代码所示,在UserDaoTemplateTest中进行查询

/**

* 利用接口里面匿名内部类的实现来讲结果集赋给对象的值移到dao层实现

* @Title: getuserList

* @Description: TODO

* @param deptNo

* @return

*/

public List<User> getUserList(){

List<User> userList = new ArrayList<User>();

String sql = "SELECT ID,NAME,AGE,SEX FROM USER ";

Object[] params = new Object[]{};

userList = this.queryForList(new RowMapper<User>(){

public User mappingRow(ResultSet rs, intrownum) throws SQLException {

User user = new User();

user.setId(rs.getInt("id"));

user.setName(rs.getString("name"));

user.setAge(rs.getInt("age"));

user.setSex(rs.getString("sex"));

returnuser;

}

}, sql, params);

returnuserList;

}

最终的代码目录结构如下,希望对大家的学习有所帮助。

四、总结

通过以上的封装过程,我们可以了解到Java封装组件的一个基本思路,有助于大家以后在用到相关的ORM组件时,对它们有一个更深得到认识,当然,本篇文章封装的代码只是冰山一角,如果需要达到像Mybatis和Hibernate等组件的高度,还有很长的一段的路要走,有兴趣的同学可以查看一下SpringJdbcTemplate的源码,其中的思想是跟它不谋而合的。

自己动手制作的一个JDBC的套件封装,求指导相关推荐

  1. 自己动手制作(DIY)一个Mini-Linux系统

    写在前面:如果此文有幸被某位朋友看见并发现有错的地方,希望批评指正.如有不明白的地方,愿可一起探讨. 本文主要目的 本文并不是要真正的去构建一个自己的Linux的系统,而是通过制作Linux系统的过程 ...

  2. 我用EXCEL VBA制作了一个成绩管理系统,请各位多多指导!

    简易成绩分析系统使用说明       四川省泸州市纳溪区大渡中学教务室制作使用 一.特色 1.限制条件少 只需把原始成绩录入或粘贴入总表即可,对总表要求极为宽松:不必整理试卷.非顺序登分;各列(包括科 ...

  3. 自己动手制作MacOS系统DMG安装镜像

    自己动手制作MacOS系统DMG安装镜像 背景 准备 从App Store获取系统安装包 创建DMG文件 后续 背景 本人因为开发需要想要在VMware中安装一台MacOS的系统,去网上下载的DMG镜 ...

  4. 虚幻4学习笔记(8)动手制作一个小游戏

    动手制作一个小游戏 新节点介绍 前期准备 搭建场景 门蓝图 灯蓝图 创建关卡蓝图 B站UP谌嘉诚课程:https://www.bilibili.com/video/BV164411Y732 新节点介绍 ...

  5. 女神节你也能自己动手制作一个漂亮的微信小游戏

    嗨!大家好,我是小蚂蚁. 这是我之前制作的一个非常漂亮的微信小游戏,你可以给予它进行改编,然后自己制作一个小游戏送给你想送的人. 我发现这篇文章每年至少可以发四次,情人节一次,女神节一次,520一次, ...

  6. 1、利用蓝牙定位及姿态识别实现一个智能篮球场套件(一)——用重写CC2541透传模块做成智能手环...

    一.预言 要实现一个智能篮球场套件,需要设计一个佩戴在篮球运动员手臂上的可以检测投篮.记步的手环,以及一套可以根据RSSI定位运动员的蓝牙定位装置.下面是大致需要的步骤: 首先,需要用CC2541透传 ...

  7. 市面上有没有靠谱的PM2.5检测仪?如何自己动手制作PM2.5检测仪

     市面上能买到的11中常见的pm2.5检测仪 网上大佬实测并不是很准,我这里没测过(全买下来有点贵,贫穷限制了我的想象力) 这些检测仪多数是复合式.多功能的空气质量检测仪.具体就不一一介绍了.这篇文章 ...

  8. 自己动手制作笔记本SP2系统安装光盘

    自己动手制作笔记本SP2系统安装光盘                                                               作者:何晓龙        微软发布官 ...

  9. 自己动手制作一门语言(1)波罗语

    自己动手制作一门语言(1)波罗语 波罗语:基于波斯(阿拉伯语)的繁衍,主要目的是二向加密语言.防止被保护的数据泄露和解密. 使用在计算机与书本中使用 语言的发音 主要以 ah .v .hv 颤音.哈音 ...

最新文章

  1. A - 数据结构实验之查找一:二叉排序树
  2. Visual Studio Code搭建TypeScript开发环境
  3. 关于Nginx location中配置proxy_pass转发时斜线'/'导致的404问题
  4. SAP Spartacus里,点击checkbox右边的span文本,不会触发checkbox勾选的原因
  5. ppt图表图表类型起始_梅科图表
  6. OpenVINO 2019 R2.0 Custom Layer Implementation for linux(1)
  7. JAVA简单的SWING及AWT
  8. 数学建模_混合赌博模型
  9. [网络安全自学篇] 二十三.基于机器学习的恶意请求识别及安全领域中的机器学习
  10. zbbz插件使用教程_CAD坐标自动标注zbbz插件非常实用(附压缩包及安装步骤)
  11. stm32码盘传感器_STM32电机测速(正交或者霍尔编码器)
  12. 我的世界服务器客户端制作,《我的世界》宇瑄服务器客户端mod
  13. Mysql 统计按学生姓名分组,总成绩前十
  14. 1分钟彻底搞懂关于nginx的proxy_pass
  15. 斐讯n1刷armbian建lnmp环境+WordPress
  16. php篮球比赛,篮球数据API接口-【篮球比赛动画直播变化数据】API调用示例代码...
  17. Defensive Programming 防御式编程(Defensive Programming)
  18. 最全Java项目合集(附源码课件),可完美运行
  19. 第十弹:网络公开课,免费学习资源,2020.07.05更新
  20. J2EE金融项目开发实战视频教程

热门文章

  1. ssh整合之六管理我们的配置文件
  2. 单用户模式迁移home家目录
  3. (紀錄)[ASP.NET MVC][jQuery]-1 純手工打造屬於自己的 jQuery GridView
  4. HDU-1128 Self Numbers 筛选
  5. Linux Kernel TCP/IP Stack — L1 Layer — NIC Controller — SKB
  6. etcd — Overview
  7. Docker 容器技术 — 软件架构
  8. Go 语言编程 — Profiling 性能分析
  9. Linux Kernel TCP/IP Stack — Socket Layer — TCP/UDP Socket 网络编程
  10. C 语言编程 — 宏定义与预处理器指令