锁定老帖子 主题:DAO设计模式(希望大家指正)
精华帖 (5) :: 良好帖 (1) :: 新手帖 (15) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-16
最后修改:2009-07-17
虽然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 courseDao=com.cvicse.dao.impl.CourseDAOImpl studentsDao=com.cvicse.dao.impl.StudentDAOImpl 8、业务层的调用方式,这里用客户端方式模拟的。在业务层通过接口的方式调用,使得DAO层和业务层能够解耦。 package com.cvicse.Test; import com.cvicse.dao.CourseDAO; import com.cvicse.daofactory.DaoFactory; /** * @author Administrator * */ public class ServiceClient { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub try { CourseDAO courseDao = (CourseDAO) DaoFactory.getInstance().getDAO( "courseDao"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
总结:在这个DAO设计模式中,涉及到很多java的基础知识,同时,也涉及太多的模式。只有灵活应用,才能体会的其中的灵活。关于DAO具体实现可以采用spring的simpetempate会更能简化其中的实现。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-07-17
自得其乐吧
|
|
返回顶楼 | |
发表时间:2009-07-17
故意弄点错误!?
1 ------------------------- cousrsDao=com.cvicse.dao.impl.CourseDAOImpl -> DaoFactory.getInstance().getDAO("courseDao"); 2 ------------------------- public Object find(String sql, Object[] args, RowMapper rowMapper)throws DaoException, DaoParameterException |
|
返回顶楼 | |
发表时间:2009-07-17
1 -------------------------
cousrsDao=com.cvicse.dao.impl.CourseDAOImpl -> DaoFactory.getInstance().getDAO("courseDao"); 的确是书写错误,因为写完就发了,大家只是看看这种设计思路是不是有问题,这个地方我修正了。 2 ------------------------- public Object find(String sql, Object[] args, RowMapper rowMapper)throws DaoException, DaoParameterException 这个问题,我觉得DaoParameterException 是继承DaoException,可以只抛DaoException |
|
返回顶楼 | |
发表时间:2009-07-17
最后修改:2009-07-17
gaojiewyh 写道 1 -------------------------
cousrsDao=com.cvicse.dao.impl.CourseDAOImpl -> DaoFactory.getInstance().getDAO("courseDao"); 的确是书写错误,因为写完就发了,大家只是看看这种设计思路是不是有问题,这个地方我修正了。 2 ------------------------- public Object find(String sql, Object[] args, RowMapper rowMapper)throws DaoException, DaoParameterException 这个问题,我觉得DaoParameterException 是继承DaoException,可以只抛DaoException 第2个问题是我看错了,以为是继承Exception的 |
|
返回顶楼 | |
发表时间:2009-07-17
因为本人也是才开始接触设计模式,想让大家给指正下其中的不足
|
|
返回顶楼 | |
发表时间:2009-07-17
楼主的Design思路很好,很清晰。
似乎跟Spring模式有重复的地方。 另外,不知道这样能否保证transaction? |
|
返回顶楼 | |
发表时间:2009-07-19
以前用这个方法.现在用spring了.数据库操作用spring提供的模板
|
|
返回顶楼 | |
发表时间:2009-07-19
感觉重复发明轮子,和spring的有什么区别,除了功能弱一些
|
|
返回顶楼 | |
发表时间:2009-07-19
的确和spring相似,
但是spring的依赖注入和策略模式不是很相似嘛,提出控制反转后又提出依赖注入,诸多的概念不感觉重复吗?我写代码只是想从中获得提高 |
|
返回顶楼 | |