代码改变世界

DAO设计模式

2011-07-18 11:36  myjava2  阅读(345)  评论(0编辑  收藏  举报
          虽然DAO模式已经有了好多的成熟的框架,但它仍然是一个比较重要的设计模式。要做一个比较合理的DAO模式,你需要对工厂模式、单例模式、模板模式、策略模式、代理模式、泛型、反射机制、输入输出、异常等知识比较熟悉。下面结合自己理解,设计一个DAO设计模式的例子,希望大家给与指正。
1、数据库连接池的工具类。
       在数据库连接池的工具类中,采用了开源的DBCP数据库连接池,调用了DataSource接口,DBCP中关于Datasource的Connection采用了动态代理的方式实现,在这里只是提出,感兴趣可以查看其源码,该工具类采用可配置的方式实现的,代码如下:
package com.cvicse.utils;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

/**
* 数据库连接池操作工具类
*    
*/

public class JDBCUtils {
private static DataSource myDataSource = null;
private JDBCUtils() {
}

static {
    try {
     Properties prop = new Properties();
//采用了类的加载获取路径下数据库的配置信息
     InputStream is = JDBCUtils.class.getClassLoader()
         .getResourceAsStream("dbcpconfig.properties");
     prop.load(is);
     myDataSource = BasicDataSourceFactory.createDataSource(prop);
    } catch (Exception e) {
     throw new ExceptionInInitializerError(e);
    }
}

/**
    * 获取数据源
    *    
    * @return
    */

public static DataSource getDataSource() {
    return myDataSource;
}

/**
    * 获取连接
    *    
    * @return
    * @throws SQLException
    */

public static Connection getConnection() throws SQLException {
    return myDataSource.getConnection();
}

/**
    * 关闭资源
    * @param rs
    * @param st
    * @param conn
    * @throws SQLException
    */

public static void free(ResultSet rs, Statement st, Connection conn)
     throws SQLException {
    try {
     if (rs != null)
        rs.close();
    } catch (SQLException e) {
     throw new SQLException();
    } finally {
     try {
        if (st != null)
         st.close();
     } catch (SQLException e) {
        throw new SQLException();
     } finally {
        if (conn != null)
         try {
            conn.close();
         } catch (Exception e) {
            throw new SQLException();
         }
     }
    }
}
}

数据库配置文件的信息如下dbcpconfig.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test123
username=root
password=

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000


#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]    
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF-8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
2、异常定义,用于处理DAO层的异常类,因为异常最好要在业务层进行处理,个人认为这DAO层异常应该在业务层进行处理,所以DAO层的必要异常都抛出。
package com.cvicse.dao.exception;

/**
*
* 定义DAO异常类
*
*/
public class DaoException extends Exception {
private static final long serialVersionUID = 1L;
/**
    * @param message
    * @param cause
    */
public DaoException(String message, Throwable cause) {
    super(message, cause);
}
/**
    * @param message
    */
public DaoException(String message) {
    super(message);
}
}

package com.cvicse.dao.exception;


/**
* 传入参数错误异常
*
*/
public class DaoParameterException extends DaoException {
private static final long serialVersionUID = 1L;

/**
    * @param message
    * @param cause
    */
public DaoParameterException(String message, Throwable cause) {
    super(message, cause);
}

/**
    * @param message
    */
public DaoParameterException(String message) {
    super(message);
}

}
3、定义要操作的pojo类,这里定义了2个pojo类
package com.cvicse.po;

/**
* 课程持久层对象
*
*/
public class Course {
private long id;
private String name;
/**
    * 构造函数类
    */
public Course() {
    this.id = 0;
    this.name = null;
}
/**
    * @param id
    * @param name
    */
public Course(long id, String name) {
    this.id = id;
    this.name = name;
}

/**
    * @return
    */
public long getId() {
    return id;
}

/**
    * @param id
    */
public void setId(long id) {
    this.id = id;
}

/**
    * @return
    */
public String getName() {
    return name;
}

/**
    * @param name
    */
public void setName(String name) {
    this.name = name;
}
}

package com.cvicse.po;

/**
* 学生持久层对象
*/
public class Student {
private long id;

private String name;

public Student() {
    this.id = 0;
    this.name = null;
}

public Student(long id, String name) {
    this.id = id;
    this.name = name;
}

public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}

4、定义对象操作的DAO接口,因为面向接口编程,定义接口目的是DAO层的操作能和业务层解耦。
package com.cvicse.dao;

import java.util.List;

import com.cvicse.dao.exception.DaoException;
import com.cvicse.po.Course;

/**
* 课程DAO层接口
*
*/

public interface CourseDAO {
        
/**
    * 获取列表
    * @return
    * @throws DaoException
    */

public List<Course> selectCourses() throws DaoException;

/**
    * 插入记录
    * @param course
    * @throws DaoException
    */

public void insertCourse(Course course) throws DaoException;
}

package com.cvicse.dao;

import java.util.List;

import com.cvicse.dao.exception.DaoException;
import com.cvicse.po.Student;

public interface StudentDAO {

/**
    * 查询方法
    * @return
    * @throws DaoException
    */

public List selectStudents() throws DaoException;

/**
    * 添加方法
    * @param student
    * @throws DaoException
    */

public void insertStudent(Student student) throws DaoException;

/**
    * 删除方法
    * @param student
    * @throws DaoException
    */

public void deleteStudent(Student student) throws DaoException;

/**
    * 修改方法
    * @param student
    * @throws DaoException
    */

public void modifyStudent(Student student) throws DaoException;
}

5、定义DAO操作的模板类,将DAO层的常用操作类进行提取。
package com.cvicse.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.cvicse.dao.exception.DaoException;
import com.cvicse.dao.exception.DaoParameterException;
import com.cvicse.dao.refactor.RowMapper;

public class DaoOperateTemplate {
/**
    * 查找单个记录对象
    *    
    * @param sql
    * @param args
    * @param rowMapper
    * @return
    * @throws DaoException
    */

public Object find(String sql, Object[] args, RowMapper rowMapper)
     throws DaoException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
     conn = JDBCUtils.getConnection();
     ps = conn.prepareStatement(sql);
     for (int i = 0; i < args.length; i++)
        ps.setObject(i + 1, args[i]);
     rs = ps.executeQuery();
     Object obj = null;
     if (rs.next()) {
        obj = rowMapper.mapRow(rs);
     }
     return obj;
    } catch (SQLException e) {
     throw new DaoException(e.getMessage(), e);
    } finally {
     try {
        JDBCUtils.free(rs, ps, conn);
     } catch (SQLException e) {
        throw new DaoParameterException(e.getMessage(), e);
     }
    }
}

/**
    * 查找多条记录对象
    *    
    * @param sql
    * @param args
    * @param rowMapper
    * @return
    * @throws DaoException
    */

public List<Object> Query(String sql, Object[] args, RowMapper rowMapper)
     throws DaoException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    List<Object> results = new ArrayList<Object>();
    try {
     conn = JDBCUtils.getConnection();
     ps = conn.prepareStatement(sql);
     for (int i = 0; i < args.length; i++)
        ps.setObject(i + 1, args[i]);
     rs = ps.executeQuery();
     Object obj = null;
     while (rs.next()) {
        obj = rowMapper.mapRow(rs);
        results.add(obj);
     }
     return results;
    } catch (SQLException e) {
     throw new DaoException(e.getMessage(), e);
    } finally {
     try {
        JDBCUtils.free(rs, ps, conn);
     } catch (SQLException e) {
        throw new DaoParameterException(e.getMessage(), e);
     }
    }
}

/**
    * 更新操作
    *    
    * @param sql
    * @param args
    * @param isGeneralKey
    * @throws DaoException
    */

public void update(String sql, Object[] args, boolean isGeneralKey)
     throws DaoException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
     conn = JDBCUtils.getConnection();
     ps = (isGeneralKey ? conn.prepareStatement(sql,
         Statement.RETURN_GENERATED_KEYS) : conn
         .prepareStatement(sql));
     for (int i = 0; i < args.length; i++)
        ps.setObject(i + 1, args[i]);
     ps.executeUpdate();
    } catch (SQLException e) {
     throw new DaoException(e.getMessage(), e);
    } finally {
     try {
        JDBCUtils.free(rs, ps, conn);
     } catch (SQLException e) {
        throw new DaoParameterException(e.getMessage(), e);
     }
    }
}
}
上面DAO通用操作类中定义接口,用于对象的转化。
package com.cvicse.dao.refactor;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
* @author Administrator
*
*/

public interface RowMapper {
    
/**
    * 映射接口
    * @param rs
    * @return
    * @throws SQLException
    */

public Object mapRow(ResultSet rs) throws SQLException;
}
6、定义具体DAO的实现,在DAO具体实现中,我们采用组合的方式引用通用类,正如设计原则中说的先考虑组合后考虑继承。所以我们在这里选择组合,而不用继承,同时继承对象的转换同样会存在问题。在每个具体DAO操作的实现类中,我们采用了策略模式。
package com.cvicse.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.cvicse.dao.CourseDAO;
import com.cvicse.dao.exception.DaoException;
import com.cvicse.dao.refactor.RowMapper;
import com.cvicse.po.Course;
import com.cvicse.util.DaoOperateTemplate;

public class CourseDAOImpl implements CourseDAO {

private DaoOperateTemplate daoTemplate = new DaoOperateTemplate();

public void insertCourse(Course course) throws DaoException {
    // TODO Auto-generated method stub
    String sql = "insert into course(id,name) values (?,?) ";
    Object[] args = new Object[] { course.getId(), course.getName() };
    daoTemplate.update(sql, args, false);
}

public List<Course> selectCourses() throws DaoException {
    // TODO Auto-generated method stub
    String sql = "select * from course where id=? ";
    Object[] args = new Object[] { 1 };
    List courseList = daoTemplate.Query(sql, args, new courseRowMapper());
    return courseList;
}

/**
    * 内部匿名类
    *    
    * @author Administrator
    *    
    */

class courseRowMapper implements RowMapper {
    public Object mapRow(ResultSet rs) throws SQLException {
     Course course = new Course();
     course.setId(rs.getLong("id"));
     course.setName(rs.getString("name"));
     return course;
    }
}
}
package com.cvicse.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.cvicse.dao.StudentDAO;
import com.cvicse.dao.exception.DaoException;
import com.cvicse.dao.refactor.RowMapper;
import com.cvicse.po.Student;
import com.cvicse.util.DaoOperateTemplate;

public class StudentDAOImpl implements StudentDAO {

private DaoOperateTemplate daoTemplate = new DaoOperateTemplate();

/*
    * (non-Javadoc)
    *    
    * @see com.cvicse.dao.StudentDAO#deleteStudent(com.cvicse.po.Student)
    */

public void deleteStudent(Student student) throws DaoException {
    // TODO Auto-generated method stub
    String sql = "delete from user where id=?";
    Object[] args = new Object[] { student.getId() };
    daoTemplate.update(sql, args, false);
}

/*
    * (non-Javadoc)
    *    
    * @see com.cvicse.dao.StudentDAO#insertStudent(com.cvicse.po.Student)
    */

public void insertStudent(Student student) throws DaoException {
    // TODO Auto-generated method stub
    String sql = "insert into student(id,name) values (?,?) ";
    Object[] args = new Object[] { student.getId(), student.getName() };
    daoTemplate.update(sql, args, false);
}

public void modifyStudent(Student student) throws DaoException {
    // TODO Auto-generated method stub
    String sql = "update student set name=? where id=? ";
    Object[] args = new Object[] { student.getName(), student.getId() };
    daoTemplate.update(sql, args, false);
}

public List selectStudents() throws DaoException {
    // TODO Auto-generated method stub
    String sql = "select * from course where id=? ";
    Object[] args = new Object[] { 1 };
    List courseList = daoTemplate.Query(sql, args, new studentRowMapper());
    return courseList;
}

/**
    * 内部匿名类
    *    
    * @author Administrator
    *    
    */

class studentRowMapper implements RowMapper {
    public Object mapRow(ResultSet rs) throws SQLException {
     Student student = new Student();
     student.setId(rs.getLong("id"));
     student.setName(rs.getString("name"));
     return student;
    }
}
}

7、我们定义工厂类,在定义工厂类,考虑到通用性,我们采用了反射机制加配置文件的形式来实现的。同时,在工厂模式中引入了饿汉式单例模式。
/**
*    
*/

package com.cvicse.daofactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
* 工厂类方法
*    
*/


public class DaoFactory {

private static DaoFactory instance = new DaoFactory();//懒汉法声明对象
private static Properties pro;// 配置文件对象

private DaoFactory() {
    try {
     // 初始化配置文件
     pro = new Properties();
     // 采用类加载器方法读取配置文件信息到字节流对象,采用类加载灵活,不用写死
     InputStream inputStream = DaoFactory.class.getClassLoader()
         .getResourceAsStream("applicationContext.properties");
     // 加载字节流对象
     pro.load(inputStream);
    } catch (IOException e) {
     throw new ExceptionInInitializerError(e);
    }
}

/**
    * 单例模式获取唯一实例
    *    
    * @return
    */

public static DaoFactory getInstance() {
    return instance;
}

/**
    * 根据配置文件的名字获取类的名字,采用反射机制获取其对象
    *    
    * @param Key
    * @return
    */

public Object getDAO(String Key) throws Exception {
    String className = (String) pro.get(Key);
    return (Class.forName(className).newInstance());
}
}
配置文件的内容如下:applicationContext.properties总结:在这个DAO设计模式中,涉及到很多java的基础知识,同时,也涉及太多的模式。只有灵活应用,才能体会的其中的灵活。关于DAO具体实现可以采用spring的simpetempate会更能简化其中的实现。