起了一个比较文艺的标题,但是仍然感觉不能 表达出接下来这个工具的文雅。 虽然这个库是前几个月写的了,但是经过了近期小项目的考验,愈发觉得这款轻量级的库应该被更多的开发者所知晓,于是“臭不要脸”地写了这篇介绍性的文章。
初衷
对于如日中天的编程语言老大哥Java而言,其拥趸者数目定然不少。
纯JDBC
基本上而言,到达一定的阶段之后,就一定会接触到数据库了。一开始写纯正的JDBC的时候,不知道别人是怎么想的,但是对我个人而言,简直是
如坐针毡。
先不论使用Statement还是使用PreparedStatement来拼凑SQL语句,单单是处理ResultSet就是个不折不扣的烦心事。
持久化框架
其实这并不是特例,大部分人都会有这么个感受。不然hibernate等这些持久化框架也不会诞生了不是。但这也带来了一些弊端。
1.首先对于新手而言,使用这些框架需要一定的学习成本,学习曲线不够平缓,这就有可能磨灭其学习的兴趣。
2.其次,对于小项目而言,动用重量级的框架,就显得“大材小用”了。虽然这并不是说部可以,却显得有点不合时宜。
轻量级持久化框架
相信大家都了解Apache的dbutils吧。大大地简化了JDBC的处理,封装了很多必须的操作。但是我个人认为它并不能称之为一个框架。
充其量是一个JDBC的Wrap版,也就是对JDBC进行了简单的封装的一个工具。
于是,在研究了其工作原理之后,结合Apache的另一个BeanUtils库,我制作了一个比较轻量级的数据库持久化框架。(姑且称之为框架吧,(^__^)
嘻嘻……)
设计思路
以我个人,不长的开发经验来看。“过犹不及,过满则亏”。所以对于框架的设计原则也应该如此。
不能什么都做,要适时的将权限放开,增加拓展。
仔细思考了,到底要拿它来做什么? 如何添加拓展?这些问题之后,我就开始着手编码了。
期间运用了 蹩脚的泛型和反射操作,自动化的解剖Bean,来解决与数据库之间的持久化和反序列化处理等等。
大体的“架构” 可用下图表示:
架构图
怎么使用?
介绍到如何使用,我觉得还是以实际的使用案例来介绍比较好,这样更有说服力。
依赖
因为这个框架依赖于apache的一些jar,所以我们需要将下列依赖添加到自己的项目中。(我个人建议在项目中新建一个lib包来放置jar文件)
数据库配置
因为JDBC是跨数据库的,所以我们只要提供针对不同的数据库厂商的jar,那么这个框架也可以跨数据库。
如上图可以看到 src目录下有一个db.cfg.xml的文件。这里面就放置了之前要手动处理的数据库配置信息了。比葫芦画瓢,按照下面的代码配置自己的环境即可。
<?xml version="1.0" encoding="UTF-8" ?> <project> <database name="mysql"> <driver>com.mysql.jdbc.Driver</driver> <url>jdbc:mysql://localhost:3306/mydb</url> <user>root</user> <password>mysql</password> </database>
</project> |
基本上来说,大家都会有专门的BaseDAO类,那么在这个类里面进行数据库配置的注册事件是最合适不过了。
import dbhelper.DbHelper;
/**
* @author 郭 璞
*
*/
public class BaseDAO {
/**
* 使用静态代码块的方式,在程序的DAO层运行之前就注册好 DbHelper ,做好对数据源的注册
*/
static {
try {
DbHelper.register();
} catch (Exception e) {
throw new RuntimeException(e + "\n 数据源未注册成功");
}
}
} |
这样,其他的DAO只需要继承这个类就可以了,我们就不用关心数据源的问题了,底层框架会自动的帮我们搞定。
正式使用
有一点需要强调的是,数据库中的字段要和Java Bean中的属性名保持高度的一致,否则框架不能正确的找到其隶属的字段信息。毕竟框架不是万能的嘛。
数据库表结构
数据库表字段
Java Bean结构
JavaBean字段
如图所示, 按照这样来设计就可以了。
从数据库获取一条记录,并转为对象
/** * 根据用户名查找其个人的详细的信息 * * @param user_name * @return */ public User selectUser(String user_name) { // 声明数据库连接对象 Connection conn = null; try { // 初始化 数据库连接对象,避免出现空指针调用异常问题 conn = DbHelper.getConn(); // 组装 SQL 查询语句 String sql = "select * from java_user where user_name= ?"; // 实例化数据库查询对象 QueryRunner queryRunner = new QueryRunner(); // 采用 泛型编程技术 ,从底层开始直接获取数据库行记录到对象的自动转化流程 User user = queryRunner.query(conn, sql, new BeanHandler<User>(User.class), user_name); // 释放数据库链接资源 DbHelper.release(conn); return user != null ? user : null; } catch (Exception e) { throw new RuntimeException(" :\n" + e); } } |
与此同时,数据库中的记录如下图:
数据库中记录信息
下面使用JUnit来测试一下, Junit测试结果
可见,确实可以自动的帮助我们反序列化数据库中的数据。
高级版
上面的小案例简单的测试了一下,单个Bean对象的获取,那么如果说ResultSet内包含多条记录呢?
这时候你可能会想,要是能自动的转换成List,然后List里面包裹着这些反序列化好的数据对象,该多好。
确实是这样的,DbHelper也做到了。(^__^) 嘻嘻……
数据库内记录
闲言少叙,咱们直接开始吧。
数据库内字段信息
JavaBean结构
按照约定,java Bean 内字段保持和数据库内表结构字段一致即可。
public class Site { private String site_name; private String site_username; private String site_password; private Integer java_user_id; private Integer java_tag_tag_id;
public String getsite_name() {
return site_name;
}
··· ··· |
然后就是 DAO层内的业务代码了,还是那简单的几步。需要注意的是,接口回调的时候new的不再是BeanHandler了,而是BeanListHandler。这和返回的结果集直接相关。
public List<Site> selectAllSites(String user_name) { Connection conn = null; try { // 实例化 数据库连接对象 conn = DbHelper.getConn(); // 拼接sql语句 String sql = "select * from java_site where java_site.java_user_id=
(select java_user.id from java_user where java_user.user_name='" + user_name + "')"; // 实例化查询器 QueryRunner queryRunner = new QueryRunner(); // 获取接口回调处理后的结果 List<Site> sites = queryRunner.query(conn, sql, new BeanListHandler<Site>(Site.class)); // 释放数据库链接资源 DbHelper.release(conn);
return sites != null ? sites : null;
} catch (Exception e) {
throw new RuntimeException(" :\n" +
e);
}
} |
然后再来看看JUnit的测试结果: JUnit List 测试结果
果不其然,还是获取到了正确的数据。这一点可以和数据库中原始的信息进行对比。
拓展
为了使得这个框架更加的灵活, 满足大部分的定制性的需求,这里给QueryRunner
额外进行了拓展。可以灵活的处理自己的业务需求。
/** * 根据给定的参数实现向数据库中给定SQL语句的update,delete,insert 操作 * * @param conn * 数据库连接对象,用户不必关心其释放问题,这里自动将其释放 * @param sql * 数据库查询语句 * @param params * 对应于SQL语句占位符的参数列表 * @throws Exception */ public void update(Connection conn, String sql, Object... params) throws Exception { PreparedStatement ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) { ps.setObject((i + 1), params[i]); } ps.executeUpdate(); DbHelper.release(conn, ps); } |
使用的时候只需要将组装好的sql语句传给query方法即可。如:
/** * 更新 网站对应的标签信息 * * @param site_name * 网站名称 * @param new_java_tag_tag_id * 新的标签信息 * @return */ public boolean updateSiteTagID(String site_name, Integer new_java_tag_tag_id) { Connection conn = null; try { conn = DbHelper.getConn(); String sql = "update java_site set java_tag_tag_id=? where site_name=?"; QueryRunner queryRunner = new QueryRunner(); Object[] params = { new_java_tag_tag_id, site_name }; // 在完成更新操作后,底层会自动的断开与数据库的链接 queryRunner.update(conn, sql, params); return true; } catch (Exception e) { throw new RuntimeException(" :\n" + e); }
} |
如此,基本上可以满足JDBC编程时遇到的情况了。
总结
其实博主本人真的是一个爱分享,热心肠的有志青年。今天写这篇文章的一个很重要的原因就是想帮助那些饱受JDBC之苦的开发人员,今早的以一种优雅的方式脱离苦海。
这篇文章从应用性的角度而言,应该算是比较详细的了。但是基本上没有讨论底层的实现。 |