通用DAO类

来源:互联网 发布:手机网站源码 html5 编辑:程序博客网 时间:2024/05/16 09:02

基础:

一般编写MVC的数据持久化层是使用DAO模式。所谓DAO,也就是Data Access Object(数据访问接口)。DAO与业务逻辑层传递的是一个实体对象,而这个实体对象是数据库表在程序中的一个映射。

所以对于数据库中要操作的每一张表,都要建立一个VO(实体对象)类,一个DAO接口,一个创建DAO的工厂类,以及一个DAO的实现类。而这些基本是一样的,如果程序需要访问多个表的时候,需要大量的重复劳动。就有了抽象出来的必要

 

思考:

首先分析DAO模式中的每一部分

VO(对象实体):这个类成员变量与数据库中每个字段一一对应,同时提供了相应是set和get方法来设置或取得数值。这个类一般是通过IDE自动生成的

DAO(数据访问接口):这个是业务逻辑层使用数据持久化层服务的一个接口

DAOFactory(工厂类):它提供了创建DAO类的一个方法,目的是更改数据持久化层实现时(比如更换数据库),不需要对业务逻辑层进行更改,只要再创建一个实现了DAO接口的类,再交给DAOFactory创建对象就可以了。为程序提供了更好的扩充性

DAOImpl(实现类):这个类实现了DAO接口。同时也是代码最多的类。

从上文分析看出,抽象DAO首先要从实现类下手。在不同的表中,DAO提供的操作基本类似,所以实现类所实现的方法基本相同,不同的地方主要有这样几个方面:

1、传递的实体类类型

2、实体类的取值赋值方法

3、SQL语句

这些问题第一个解决起来最容易,可以通过Java的泛型解决。第三个也比较容易,可以在创建时传入字段,执行时生成SQL语句。只有第二个最困难,因为并不知道实体类提供哪些方法,以及方法的返回值和参数列表,怎么样去调用未知的方法?

 

关键技术:

Java为我们提供了反射机制,来进行一些对象的操作。

 

比如通过这个方法可以让加载器加载一个类:

Class c = Class.forName(className);

得到Class对象以后,我们可以调用它的公有的无参构造方法,获得它的一个实例

Object o = c.newInstance();

也可以获得描述它所有方法的一个数组

Method[] m = c.getMethods();

也可以得到某一个方法的方法名

System.out.println(m[0].getName());
或者对一个方法的参数列表及返回值类型的描述
System.out.println(m[0].toGenericString());
通过以上方法,可以分析出哪个方法是我们想要的方法,我们怎么调用它呢
public Object invoke(Object obj, Object... args)

参数:
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
返回:
使用参数 argsobj 上指派该对象所表示方法的结果

通过这个方法,在第一个参数传入一个创建的对象,2~n个参数传入调用方法的参数,就可以调用这个方法,
同时也可以接受得到Object类型的返回值。想了解更详细的内容可以参考JDK API
理解了以上的函数,我们可以动手编写这个类了
 

详细设计:

为了更规范,我们希望用户创建的VO类都有一个标志,所以我们创建一个接口,这个接口不需要实现任何方法,只起一个标记作用
package generalDAO;/** * 实体类标记接口 *  * @author 石莹 * */public interface VO {}
 
因为是通过方法名的字符串来确定我们究竟要调用哪个方法,如果VO类并不是符合我们规范的类,应该抛出一个异常
package generalDAO;/** * VO类不符合规范异常 * @author 石莹 * */public class VOClassIllegalException extends Exception {private static final long serialVersionUID = 861316221880365983L;public VOClassIllegalException() {}public VOClassIllegalException(String str) {super(str);}}
 
下面就是主要的部分,注释已经非常清楚
package generalDAO;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.ArrayList;import connectionPool.ConnectionPool;/** * 通用DAO类 *  * 通过这个类,只需要按照命名约定编写一个实现了VO接口的实体类,就能操作数据库中相应的表 * 方便的同时必然带来性能的降低。类的实现使用了Java的一些底层特性,可以供初学者学习 *  * 使用DAO类必须遵循以下约定: *  * 1、所有实体类必须实现VO接口 * 2、实体类的类名必须和数据库表名一致 * 3、当实体类中同时存在getXXX()和setXXX()方法时,XXX可被识别为一个字段 * 4、实体类中的字段名必须和数据库中的字段名一致 * 5、主键必须使用“表名 + Id”的命名规则 * 6、目前支持的类型:float,String,int *  * 另:使用需要传入我编写的一个简单的数据库连接池,请参考这里 * http://blog.csdn.net/lingdushanke/archive/2010/09/11/5877163.aspx *  * (2010.9.19) *  * @author 石莹 * @version 1.0 * */public class DAO {/** 数据库连接池 */private ConnectionPool connectionPool = null;/** * 加载后的实体类 */@SuppressWarnings("unchecked")private Class voClass = null;/** 表名 */private String tableName = null;/** 表中的字段名 */private ArrayList fieldName = new ArrayList();/** VO类中的方法名 */private ArrayList methodName = new ArrayList();/** * 构造方法 * @param voName VO类的类名 * @param connectionPool ConnectionPool类数据库连接池 * @throws ClassNotFoundException 类没有找到,抛出异常 * @throws VOClassIllegalException 如果VO类不符合规则,抛出异常 */public DAO(String voName, ConnectionPool connectionPool) throws ClassNotFoundException, VOClassIllegalException {//加载VO类voClass = Class.forName(voName);//读取方法名for (Method me : voClass.getDeclaredMethods()) {methodName.add(me.getName());}//读取数据库表名StringBuffer buffer = new StringBuffer();int i = voName.length()-1;while (i >= 0 && voName.charAt(i) != '.') {buffer.insert(0, voName.charAt(i--));}tableName = buffer.toString();//获取字段名for (int i1=0; i1if (methodName.get(i1).startsWith("get")) {String f = methodName.get(i1).substring(3);if (methodName.contains("set" + f)) {fieldName.add(f);}}}//检查是否存在主键,并移到末尾boolean keyExists = false;for (i=0; iif (fieldName.get(i).compareToIgnoreCase(tableName + "Id") == 0) {keyExists = true;break;}}if (!keyExists) {throw new VOClassIllegalException();} else {fieldName.add(fieldName.remove(i));}//创建数据库连接池this.connectionPool = connectionPool;}/** * 从表单获取所有记录 * @return 保存所有记录的ArrayList * @throws InvocationTargetException  * @throws IllegalAccessException  * @throws InstantiationException  * @throws SQLException  * @throws IllegalArgumentException  */public ArrayList getAll() throws IllegalArgumentException,SQLException, InstantiationException, IllegalAccessException,InvocationTargetException {String sql = "select * from " + tableName;return sqlQuery(sql);}/** * 查询一条记录 * @param id 记录ID * @return 表示记录的VO对象 * @throws InvocationTargetException  * @throws IllegalAccessException  * @throws InstantiationException  * @throws SQLException  * @throws IllegalArgumentException  */public VO getById(String id) throws IllegalArgumentException, SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {String sql = "Select * from " + tableName + " where " + tableName + "id = '" + id + "'";ArrayList list = null;list = sqlQuery(sql);if (list.size() == 0) return null;else return list.get(0);}/** * 插入一条数据 * @param o 要插入的数据 * @throws SQLException  * @throws InvocationTargetException  * @throws IllegalAccessException  * @throws IllegalArgumentException  */public void insert(VO o) throws SQLException, IllegalArgumentException,IllegalAccessException, InvocationTargetException {//创建插入的SQL语句StringBuffer buffer = new StringBuffer();buffer.append("insert into " + tableName + " (");for (String s : fieldName) {buffer.append(s + " ,");}buffer.deleteCharAt(buffer.length()-1);buffer.append(")");buffer.append(" values ( ");for (@SuppressWarnings("unused") String s : fieldName) {buffer.append("? ,");}buffer.deleteCharAt(buffer.length()-1);buffer.append(")");String sql = buffer.toString();//执行SQL语句Connection conn = connectionPool.get();PreparedStatement pstat = conn.prepareStatement(sql);setValue(pstat, o);pstat.executeUpdate();}/** * 更新一条记录 * @param o 要更新的VO对象,其中主键保存要更新的主键,其它值替代数据库中的当前值 * @throws SQLException  * @throws InvocationTargetException  * @throws IllegalAccessException  * @throws IllegalArgumentException  */public void update(VO o) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException,SQLException {//创建插入的SQL语句StringBuffer buffer = new StringBuffer();buffer.append("update " + tableName + " set ");for (int i=0; i = ? ,");}buffer.deleteCharAt(buffer.length()-1);buffer.append(" where " + fieldName.get(fieldName.size()-1) + " = '?'");String sql = buffer.toString();//执行SQL语句Connection conn = connectionPool.get();PreparedStatement pstat = conn.prepareStatement(sql);setValue(pstat, o);pstat.executeUpdate();}/** * 从数据库删除一条记录 * @param id 记录的ID * @throws SQLException */public void delete(String id) throws SQLException {String sql = "delete from " + tableName + " where " + tableName + "id = '" + id + "'";System.out.println(sql);sqlUpdate(sql);}/** * 从结果集向对象设置值 * @param rs 从数据库中查询得到的结果集 * @return 结果集设置到的对象 * @throws InstantiationException  * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws SQLException */protected VO getValue(ResultSet rs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SQLException {VO o = (VO) voClass.newInstance();//每次得到一个字段名for (String s : fieldName) {//计数器int i=0;//从方法名里查找当前字段的set方法for (i=0; iif (methodName.get(i).equals("set" + s))break;}//得到找到的方法名的Method对象Method me = voClass.getMethods()[i];//获得当前对象set方法的描述信息String gstr = me.toGenericString();//解析描述信息,得到参数类型for (i=0; iif (gstr.charAt(i) == '(') {i++;break;}}StringBuffer buffer = new StringBuffer();while (i < gstr.length() && gstr.charAt(i) != ')') {buffer.append(gstr.charAt(i++));}String rstr = buffer.toString();//根据参数类型,调用结果集不同的方法获取值if (rstr.equals("java.lang.String")) {me.invoke(o, rs.getString(s));} else if (rstr.equals("int")) {me.invoke(o, rs.getInt(s));} else if (rstr.equals("float") || rstr.equals("double")) {me.invoke(o, rs.getFloat(s));}}return o;}/** * 将一个VO对象的值设置到PreparedStatement对象中 * @param pstmt PreparedStatement对象 * @param o VO对象 * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InvocationTargetException * @throws SQLException */protected void setValue(PreparedStatement pstmt, VO o)throws IllegalArgumentException, IllegalAccessException,InvocationTargetException, SQLException {for (int index=0; index//计数器int i=0;//从方法名里查找当前字段的set方法for (i=0; iif (methodName.get(i).equals("get" + s))break;}//得到找到的方法名的Method对象Method me = voClass.getMethods()[i];//获得当前对象set方法的描述信息String gstr = me.toGenericString();//解析描述信息,得到参数类型for (i=0; iif (gstr.charAt(i++) == ' ') {break;}}StringBuffer buffer = new StringBuffer();while (i//根据VO中get方法返回值调用不同函数if (rstr.equals("java.lang.String")) {String str = (String) me.invoke(o);pstmt.setString(index+1, "'" + str + "'");} else if (rstr.equals("int")) {Integer in = (Integer) me.invoke(o);pstmt.setInt(index+1, in);} else if (rstr.equals("float") || rstr.equals("double")) {Double dou = (Double) me.invoke(o);pstmt.setFloat(index+1, dou.floatValue());}}}/** * 执行一条查询的SQL语句 * @param sql 要执行的SQL语句 * @return 得到的结果的实体类列表 * @throws SQLException * @throws IllegalArgumentException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException */public ArrayList sqlQuery(String sql) throws SQLException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {ArrayList data = new ArrayList();Connection conn = connectionPool.get();PreparedStatement pstat = conn.prepareStatement(sql);ResultSet rs = pstat.executeQuery();while (rs.next()) {VO o = getValue(rs);data.add(o);}connectionPool.close(conn);return data;}/** * 执行一条用来修改的SQL语句 * @param sql 用来执行的SQL语句 * @throws SQLException */public void sqlUpdate(String sql) throws SQLException {Connection conn = connectionPool.get();PreparedStatement pstat = conn.prepareStatement(sql);pstat.executeUpdate();}}
 
编写完成以后打包发布
 

测试:

新建一个工程,导入connectionPool.jar和generalDAO.jar包,不用多说。
首先创建数据库,语句如下
CREATE TABLE [testVO] ([testVoId] [char] (10) COLLATE Chinese_PRC_CI_AS NULL ,[username] [char] (10) COLLATE Chinese_PRC_CI_AS NULL ,[password] [char] (10) COLLATE Chinese_PRC_CI_AS NULL ) ON [PRIMARY]GO
 
根据这个表在程序中编写一个VO类
import generalDAO.VO;public class TestVO implements VO {private String testVoId;private String username;private String password;public String getTestVoId() {return testVoId;}public void setTestVoId(String testVoId) {this.testVoId = testVoId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
下面编写主程序
 
import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.sql.SQLException;import generalDAO.DAO;import generalDAO.VOClassIllegalException;import connectionPool.ConnectionPool;import connectionPool.ConnectionPoolFactory;public class test {/** * @param args * @throws VOClassIllegalException  * @throws ClassNotFoundException  * @throws InvocationTargetException  * @throws IllegalAccessException  * @throws SQLException  * @throws IllegalArgumentException  * @throws InstantiationException  */public static void main(String[] args) throws ClassNotFoundException, VOClassIllegalException,IllegalArgumentException, SQLException, IllegalAccessException, InvocationTargetException, InstantiationException {/*ConnectionPool connectionPool = ConnectionPoolFactory.getSQLServerConnectionPool();connectionPool.setConnect("jdbc:sqlserver://localhost:7788;databaseName=test","sa", "admin");DAO dao = new DAO("TestVO",connectionPool);TestVO tv = new TestVO();tv.setTestVoId("id");tv.setUsername("name");tv.setPassword("password");dao.insert(tv);*///dao.delete("id");/*TestVO tv = new TestVO();tv.setPassword("123");tv.setUsername("sss");tv.setTestVoId("id");dao.update(tv);*///System.out.println(((TestVO)dao.getById("id")).getUsername());}}
测试结果需要对照SQL查询分析器来看,这里就不贴了。
可以看到通过这个类可以极大的减少代码量,但是带来了性能的降低。
 
原创粉丝点击