读书人

(模板方法的施用)持久层操作的java代

发布时间: 2012-09-23 10:28:11 作者: rapoo

(模板方法的运用)持久层操作的java代码

2008年刚从学校毕业就应聘到了一家外包小公司工作
对加拿大的客户开发一些web项目
公司里算我就2个人是做java的
于是我们变成了一个人身兼数个项目的程序员
经理只给我们客户的需求
然后数据库设计、编码等事你一个人搞定。。。

我当时刚出来对java的理解还没有那么深,没有商业项目的任何经验就让我搞着搞那。
当时连数据库连接的操作我都很苦恼,是用简单的jdbc还是hibernate呢,
最后为了方便而达到目的我选用了前者。赶项目的效率还是很重要的。

我给出当时自己写的操作数据库的代码
这家公司大多都是.net的程序所以用的数据库也为sql server 2005
所以不知道这样写是不是会有很多问题,因为我总觉得有问题。
3年后虽然已经离开公司但再回头看这些代码,我还是觉得有问题,所以必须发出来大家审视一下。

这些代码是2008年4月刚出道时自己写的。当时正在读老师推荐给我的java设计模式,看到了template这个模式,于是在工作中突然想到可以把这个模式应用在操作数据库的代码上
于是就产生了以下代码。




第一个是操作数据库方法的抽象接口,
其实这个接口也很简单,定义了那么几个方法,说白了就是操作数据库的。
为什么要写成泛型的接口,为了就是后面大家的业务有针对性,一个实体一个业务功能类。


package com.yd.idao;import java.util.List;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import com.yd.support.JDataSet;/** * 一个定义了所有我所需要的数据库操作的方法接口, * 为什么定义为抽象,我自己都搞不清楚, * 这里定义了泛型,这个很关键,你会看到泛型在这里的使用 * @author kanny * * @param <T> */public abstract interface ISqlHelper<T> {/** * 执行sql语句,大多为单句插入语句 * @param sql单句的sql语句 * @param params插入的参数 * @param only但为true时,sql语句为查询数量的查询语句 * @return * @throws SQLException */public boolean execute(String sql, Object[] params, boolean only) throws SQLException;/** * 执行sql的批处理 * @param sqlBatch多条sql语句 * @return * @throws SQLException */public boolean executeBatch(String[] sqlBatch) throws SQLException;/** * 执行存储过程 * @param procName存储过程名称 * @param params存储过程说需要的参数 * @return * @throws SQLException */public boolean executeCall(String procName, Object[] params) throws SQLException;/** * 查询一行数据封装成java的实体 * @param sqlsql语句 * @param paramssql条件参数 * @param viewName视图名字,在查询多表关联的数据时用于区分 * @param executeCall是否是存储过程,如果为true,第一个sql的参数为存储过程名称 * @return * @throws SQLException */public T find(String sql, Object[] params, String viewName, boolean executeCall) throws SQLException;/** * 查询多行数据封装成java的实体加入一个List里 * @param sqlsql语句 * @param paramssql条件参数 * @param viewName视图名字,在查询多表关联的数据时用于区分,循环封装实体比单一的石头封装要复杂 * @param executeCall是否是存储过程,如果为true,第一个sql的参数为存储过程名称 * @return * @throws SQLException */public List<T> findList(String sql, Object[] params, String viewName, boolean executeCall) throws SQLException;/** * 为了方便操作,我还特意写定义了这个返回ResultSet的方法,便于直接操作 * @param sql * @param params * @param executeCall * @return * @throws SQLException */public ResultSet returnResultSet(String sql, Object[] params, boolean executeCall) throws SQLException;/** * 我的底层分页方法,我会给出具体代码,但这里用的只是sql server 2008的数据库分页 * @param sql * @param orderby排序列 * @param currentPage当前页 * @param pageSize每页多少行 * @return * @throws SQLException */public List<T> findListAsPager(String sql, String orderby, int currentPage, int pageSize) throws SQLException;/** * 后来为了方便操作,想朋友要来了这个JDataSet类,类似.net的DataTable的作用 * @param sql * @param params * @param fillColumnNames是否填充类名,请看方法代码 * @param executeCall * @return */public JDataSet getJDataSet(String sql, Object[] params, boolean fillColumnNames, boolean executeCall);/** * 由于有了JDataSet这个类,于是我有写了调用这个类的分页方法 * @param sql * @param orderby * @param currentPage * @param pageSize * @return * @throws SQLException */public JDataSet getJDataSetAsPager(String sql, String orderby, int currentPage, int pageSize) throws SQLException;/** * 为了方便起见,我多写了一个直接传入ResultSet而封装JDataSet的多余方法 * @param rs * @param fillColumnNames * @return * @throws SQLException */public JDataSet loadJDataSet(ResultSet rs, boolean fillColumnNames) throws SQLException;/** * 得到查询数据的行数 * @param sql * @return * @throws SQLException */public int getRowCount(String sql) throws SQLException;/** * 请看源码 * @param rs * @param column * @return * @throws SQLException */public String changeFont(ResultSet rs, String column) throws SQLException;/** * 得到连接 * @return * @throws SQLException */public Connection returnConn() throws SQLException; /** * 清楚所有数据库操作对象 * @throws SQLException */public void clearAllsql() throws SQLException; }




第2个类是实现这个抽象接口的抽象模版方法类
这个类最为关键,它肯定是实现了ISqlHepler.java里面的所有的方法。

其中:

protected abstract T loadDataBean(ResultSet rs, String viewName) throws SQLException;

protected abstract T loadDataBeanSelf(ResultSet rs) throws SQLException;

这2个方法是SqlHelper.java里留出来了,就是为了大家可以自己封装实体javabean来用

package com.yd.dao;import java.sql.*;import java.util.*;import com.yd.db.DBConnPoolMgr;import com.yd.idao.ISqlHelper;import com.yd.support.JDataSet;public abstract class SqlHelper<T> implements ISqlHelper<T> {protected java.sql.Connection conn = null;protected java.sql.PreparedStatement pst = null;protected java.sql.Statement st = null;protected java.sql.CallableStatement cs = null;protected java.sql.ResultSet rs = null;protected java.sql.ResultSetMetaData rm = null;public Connection returnConn() throws SQLException {                  //DBConnPoolMgr是自己写的一个简单连接池类,用来得到连接,我后面会给出这个类的代码return (conn = DBConnPoolMgr.getInctence().getConnect());}private PreparedStatement returnPst(String sql, Object[] params) throws SQLException {if (conn == null || conn.isClosed()) conn = returnConn();pst = conn.prepareStatement(sql);if (params != null)for (int i = 0; i < params.length; i++)pst.setObject(i + 1, params[i]);return pst;}protected CallableStatement returnCs(String procName, Object[] params) throws SQLException {if (conn == null || conn.isClosed()) conn = returnConn();String call = "";if (params != null) {call = "{call " + procName + "(";for (int c = 0; c < params.length - 1; c++)call += "?,";call += "?)}";} elsecall = "{call " + procName + "()}";cs = conn.prepareCall(call);if (params != null)for (int i = 0; i < params.length; i++)cs.setObject(i + 1, params[i]);return cs;}public void clearAllsql() {try { if (rs != null) rs.close();if (cs != null) cs.close();if (st != null) st.close();if (pst != null) pst.close();if (conn != null) {DBConnPoolMgr.getInctence().returnConnect(conn);}rs = null;cs = null;st = null;pst = null;} catch (SQLException ex) { ex.printStackTrace(); }}public boolean execute(String sql, Object[] params, boolean only) {boolean bVal = false;try {if (only) {rs = returnPst(sql, params).executeQuery();while (rs.next()) bVal = true;} else {returnPst(sql, params).executeUpdate();bVal = true;}} catch (SQLException ex) { ex.printStackTrace(); } finally { clearAllsql(); }return bVal;}public boolean executeBatch(String[] sqlBatch) {boolean bVal = false;try {conn = returnConn();st = conn.createStatement();boolean autoCommit = conn.getAutoCommit();for (int i = 0; i < sqlBatch.length; i++) {if (sqlBatch[i] != null && !sqlBatch[i].equals(""))st.addBatch(sqlBatch[i] + ";");}conn.setAutoCommit(false);st.executeBatch();conn.commit();conn.setAutoCommit(autoCommit);bVal = true;} catch (SQLException ex) {try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); }ex.printStackTrace();} finally { clearAllsql(); }return bVal;}public boolean executeCall(String procName, Object[] params) {boolean bVal = false;try {returnCs(procName, params).executeUpdate();bVal = true;} catch (Exception ex) { ex.printStackTrace(); } finally { clearAllsql(); }return bVal;}public T find(String sql, Object[] params, String viewName, boolean executeCall) {T t = null;try {if (executeCall) rs = returnCs(sql, params).executeQuery();else rs = returnPst(sql, params).executeQuery();t = loadResultSet(rs, viewName);} catch (Exception ex) { ex.printStackTrace(); } finally { clearAllsql(); }return t;}public List<T> findList(String sql, Object[] params, String viewName, boolean executeCall) {List<T> lt = null;try {if (executeCall) rs = returnCs(sql, params).executeQuery();else rs = returnPst(sql, params).executeQuery();lt = loadList(rs, viewName);} catch (Exception ex) { ex.printStackTrace(); } finally { clearAllsql(); }return lt;}public ResultSet returnResultSet(String sql, Object[] params, boolean executeCall) {try {if (executeCall) rs = returnCs(sql, params).executeQuery();else rs = returnPst(sql, params).executeQuery();} catch (Exception ex) { ex.printStackTrace(); }return rs;}private T loadResultSet(ResultSet rs, String viewName) throws SQLException {T t = null;if (rs != null) while (rs.next()) t = loadDataBean(rs, viewName);return t;}private List<T> loadList(ResultSet rs, String viewName) throws SQLException {List<T> tlist = new ArrayList<T>();if (rs != null) while (rs.next()) tlist.add(loadDataBean(rs, viewName));return tlist;}public String changeFont(ResultSet rs, String column) throws SQLException {return rs.getString(column) == null ? "" : rs.getString(column);}public int returnColumnCount(ResultSet rs) throws SQLException {return rs.getMetaData().getColumnCount();}         //两个非常关键的模版方法,继承此类的操作类都要实现这2个方法,我到时候会给出操作类protected abstract T loadDataBean(ResultSet rs, String viewName) throws SQLException;protected abstract T loadDataBeanSelf(ResultSet rs) throws SQLException;public List<T> findListAsPager(String sql, String orderby, int currentPage, int pageSize) {List<T> lt = null;try {String strVal = strPager(sql, orderby, currentPage, pageSize);rs = returnPst(strVal, null).executeQuery();lt = loadList(rs, "");} catch (SQLException ex) { ex.printStackTrace(); } finally { clearAllsql(); }return lt;}         //因为用的sql server 2008所以只写了这个数据库的分页private String strPager(String sql, String orderby, int currentPage, int pageSize) {int start = 1;if (currentPage > 1) start = (currentPage - 1) * pageSize + 1;int end = start + pageSize - 1;String sqlColumn = "*";String sqlDo = "";if(sql.indexOf("@#") != -1) {String []sqlArr = sql.split("@#");sqlColumn = sqlArr[0];sqlDo = sqlArr[1];} else {sqlColumn = "*";sqlDo = sql;}String strVal = "select * from (select " + sqlColumn + ",ROW_NUMBER()";strVal += " Over(order by " + orderby + ")";strVal += " as rowNum " + sqlDo + ")";strVal += " as myTable where rowNum between " + start + " and " + end;return strVal;}public int getRowCount(String sql) throws SQLException {String sqlDo = "";if(sql.indexOf("@#") != -1) {String []sqlArr = sql.split("@#");sqlDo = sqlArr[1];} else sqlDo = sql;int count = 0;try {rs = this.returnResultSet("select count(*) " + sqlDo, null, false);while (rs.next()) count = rs.getInt(1);} catch (Exception ce) { ce.printStackTrace();} finally { clearAllsql(); }return count;}public JDataSet getJDataSetAsPager(String sql, String orderby, int currentPage, int pageSize) {String strVal = strPager(sql, orderby, currentPage, pageSize);return getJDataSet(strVal, null, true, false);}public JDataSet getJDataSet(String sql, Object[] params, boolean fillColumnNames, boolean executeCall) {JDataSet jds = null;try {if (executeCall) rs = returnCs(sql, params).executeQuery();else rs = returnPst(sql, params).executeQuery();jds = loadJDataSet(rs, fillColumnNames);} catch (Exception ex) { ex.printStackTrace(); } finally { clearAllsql(); }return jds;}public JDataSet loadJDataSet(ResultSet rs, boolean fillColumnNames) {JDataSet jds = new JDataSet();try {int columnCount = returnColumnCount(rs);if (fillColumnNames) {String[] columnNames = new String[columnCount];String[] columnTypeNames = new String[columnCount];for (int i = 0; i < columnCount; i++) {columnNames[i] = rs.getMetaData().getColumnName(i + 1);columnTypeNames[i] = rs.getMetaData().getColumnTypeName(i + 1);}jds.setColumnNames(columnNames);jds.setColumnTypeNames(columnTypeNames);}while (rs.next()) {String[] row = new String[columnCount];for (int i = 0; i < columnCount; i++)row[i] = rs.getString(i + 1) == null ? "" : rs.getString(i + 1).trim();jds.addRow(row);}} catch (Exception ex) { ex.printStackTrace(); }return jds;}}



现在来说下怎么用这个类
比如,你有一个学生类Student.java里面有id,name,age3个属性

public Class Student{private int id;private String name;private int age;以下get和set方法省略...}



你要封装它,你就可以自己定义一个IStudent.java接口extends那个ISqlHelper.java接口
然后来里面定义插入,查询全部,和按id查的功能函数,象如下写的一样
<

当然这样定义很死,最好的方法,就是整理抽象出所有业务类的公用方法,比如,插入,修改,删除。
然后把这些方法在一个公用的操作接口中定义,这个公用的操作接口可以继承ISqlHelper.java接口
然后以后业务类或是数据实体操作类都可以实现这个公用的操作接口。

>

public interface IStudent extends ISqlHelper<Student>{//插入Studentpublic boolean insertStudent(Student stu);//查询全部Studentpublic List<Student> findStudent();//按id查询某一个Studentpublic Student findStudentAsId(int stuid);}



并写一个StudentBO.java业务类extends那个SqlHelper和自己定义的IStudent接口
象如下写的一样:

public Class StudentBO extends SqlHelper<Student> implements IStudent{protected Student loadDataBean(ResultSet rs, String viewName) throws SQLException;{Student stu = loadDataBeanSelf(ResultSet rs);return stu;}protected Student loadDataBeanSelf(ResultSet rs) throws SQLException;{Student stu = new Student();stu.setId(rs.getInt("id"));stu.setName(rs.changeFont(rs,"name"));stu.setAge(rs.getInt("age"));return stu;}//实现:插入Student的函数public boolean insertStudent(Student stu){String sql = "insert into Student(id,name,age) values(?,?,?)";Object [] params = new Object[]{stu.getId(),stu.getName,stu.getAge};return this.execute(sql, params, false);}//实现:查询全部Studentpublic List<Student> findStudent(){String sql = "select * from Student";return this.findList(sql, null, "",false);}//实现:按id查询某一个Studentpublic Student findStudentAsId(int stuid){String sql = "select * from Student where id=" + stuid;return this.find(sql, null, "",false);}}


loadDataBean,loadDataBeanSelf是从SqlHelper里继承下来的抽象方法。
必须实现。这就是利用了模版方法模式写出来的。
也就是说,以后你自己写函数的时候,只要在接口里定义,
然后在这样的BO类里写实现它,而且就写那么一点语句就完成了。大大省时省力。

好了这样就完成了,代码是不是看上去又清楚又整洁,如果查找和维护。而且大大减少了那些功能代码和封装象Student.java这样数据实体的代码。一次封装无须在写。

大家肯定有疑问,那就是
this.execute
this.findList
this.find
三个函数哪来的,里面都做了些什么
首先要说他们3个肯定是SqlHelper里被实现过的函数
至于他们怎么实现的,你仔细看下就全明白了。


为什么要多出一个loadDataBean来,大家都看到了,他有一个参数viewName,这里就是用到这个参数的地方,因为大家操作数据库不可能就是那么一张表的操作,有时候会有2到3张表联查,那么用这个参数来判断是哪个函数查出不一样的结果,那么你在这个函数里利用这个参数引进别的xxxBO的loadDataBeanSelf函数,不就可以不用再次重新写那些讨厌的实体封装代码了吗。重用性大大提升。

注意一点,这里的查询是只能全查,如果只需要几个字段,就只能手动自己写咯,其实我是想写一个通用的想查几个字段就几个字段的封装方法,但我懒,所以到现在都没去写

其实还有很多应用和问题,这些我在这里也说不完,希望大家多多交流。


我还会给出我自己当初写的数据库连接池类,希望这些简单的代码能给java新手以帮助。
而且如果大家觉得有问题有意义,一定要发帖指出。哪里好哪里不好
希望大家多多给出看法,多多益善。

在这里还要感谢论坛中yangguo兄,其实我当时写这段代码的主要目的
就是dbutils框架所实现的一个最重要功能
只提交sql语句,然后的数据自动填充到所需要的实体中。而不要在写那么多繁琐的实体封装代码。

这个是源码例子,希望大家能明白这样写的用意,值得探讨。
70 楼 kanny87929 2011-04-05 ekian 写道刚出道已有这功力了。。LZ这是多久的积累啊?
没工作前一直在看设计模式,工作了就非常想把自己唯一看明白的几个模式运用带项目中,于是就有了这些代码的设计 71 楼 linluvsls 2011-04-06 我也刚出道 偶杂就写不出来 72 楼 kanny87929 2011-04-06 linluvsls 写道我也刚出道 偶杂就写不出来

也许你没有好好学习 73 楼 zhangwe415 2011-04-06 好吧!我没承认没好好学习,现在来学习你的。楼主有什么学习经验推荐没,项目上学习的东西有限,主要想自己发展! 74 楼 kanny87929 2011-04-06 zhangwe415 写道好吧!我没承认没好好学习,现在来学习你的。楼主有什么学习经验推荐没,项目上学习的东西有限,主要想自己发展!

我现在学习的目标只集中在

原代码和数据结构和算法上
原代码,看的项目是dbutils1.3和tomcat5.5和7,还下载了lucene不过我估计看不到最近
还有就是基础
java.lang下的各类,Object, Class
java.util下的各个工具类,比如ArrayList和HashMap
java.io各个类的实现

数据结构和算法上我看的是
java数据结构和算法的第2版
这个很有意义,以后我肯定会把各种算法用java整理出来发布在论坛 75 楼 shansun123 2011-04-06 嗯嗯 刚出道就能写出这么漂亮的代码 值得赞~! 76 楼 kanny87929 2011-04-06 shansun123 写道嗯嗯 刚出道就能写出这么漂亮的代码 值得赞~!
谢楼上称赞 77 楼 c267ray 2011-04-06 kanny87929 写道lgm277531070 写道68.
69. /**
70. * 为了方便操作,我还特意写定义了这个返回ResultSet的方法,便于直接操作
71. * @param sql
72. * @param params
73. * @param executeCall
74. * @return
75. * @throws SQLException
76. */
77. public ResultSet returnResultSet(String sql, Object[] params, boolean executeCall) throws SQLException;


我被这个雷到了,还能返回ResultSet结果集?????
那方法里面的结果集你没关闭么???如果关闭了,还怎么能返回呢???


这个方法的ResultSet对象,是没有关闭的,所以要在自己的方法里自行关闭。

是否可以参考CachedRowSet? 78 楼 yjp0501 2011-04-06 我现在写的都只是这样呢,呵呵,其实我现在才出来,大学都还没有毕业呢,追赶中!!! 79 楼 kanny87929 2011-04-07 c267ray 写道kanny87929 写道lgm277531070 写道68.
69. /**
70. * 为了方便操作,我还特意写定义了这个返回ResultSet的方法,便于直接操作
71. * @param sql
72. * @param params
73. * @param executeCall
74. * @return
75. * @throws SQLException
76. */
77. public ResultSet returnResultSet(String sql, Object[] params, boolean executeCall) throws SQLException;


我被这个雷到了,还能返回ResultSet结果集?????
那方法里面的结果集你没关闭么???如果关闭了,还怎么能返回呢???


这个方法的ResultSet对象,是没有关闭的,所以要在自己的方法里自行关闭。

是否可以参考CachedRowSet?

有机会我会去看 80 楼 kanny87929 2011-04-07 yjp0501 写道我现在写的都只是这样呢,呵呵,其实我现在才出来,大学都还没有毕业呢,追赶中!!!


这里主要的思想体现的模板方法模式的运用。这个才是关键,如果只是把这些代码整理到一个类里意义就不大了 81 楼 Speak-shuai 2011-04-07 lz太牛了 82 楼 kanny87929 2011-04-07 Speak-shuai 写道lz太牛了
谢谢赞赏。。。。 83 楼 doublelcf 2011-04-21 真的写的不错,java思想很重要,有思想才产生出作品 84 楼 kanny87929 2011-04-21 doublelcf 写道真的写的不错,java思想很重要,有思想才产生出作品
这帖子又跳起来了 85 楼 日照西桥 2011-05-25 我也要赞一下楼主 86 楼 kanny87929 2011-05-26 日照西桥 写道我也要赞一下楼主
不错啊,第一篇文章写我这里了 87 楼 chakey 2011-05-27 刚出道 就能写这么好质量的代码 很厉害了 88 楼 zhouxingfu520 2011-06-22 怎么我情况和你差不多 公司两人搞java 其他都是.net 89 楼 kanny87929 2011-06-23 zhouxingfu520 写道怎么我情况和你差不多 公司两人搞java 其他都是.net

很多公司都是这个情况

读书人网 >软件架构设计

热点推荐