`

DAO设计模式(希望大家指正)

阅读更多

   虽然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会更能简化其中的实现。

 

分享到:
评论
51 楼 yuantong 2010-06-27  
xiaojunjava 写道
自得其乐吧

50 楼 slaser 2010-06-25  
smartinvoke 写道
代理量太大了,看得我头发晕!
spring在一定程度上会引起类的爆炸效应
一个不做j2ee的java程序员飘过。。。

啥叫Ioc, 你写业务代码的看那些有什么用。
49 楼 smartinvoke 2010-06-22  
代理量太大了,看得我头发晕!
spring在一定程度上会引起类的爆炸效应
一个不做j2ee的java程序员飘过。。。
48 楼 ketty303 2010-06-22  
看了以后有种行云流水的感觉!!
2个字:流畅!
47 楼 endlesshb 2010-05-19  
    
  
    /**  
     * 查找多条记录对象  
     *   
     * @param sql  
     * @param args  
     * @param rowMapper  
     * @return  
     * @throws DaoException  
     */  
    这个方法弱弱的问一下楼主,如果是你写的一句SQL根据条件查询出来是100条,此时你如何确定你返回的List里面
     就是100个元素?
    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  
     */
    update方法都是清一色的void试问下你如果判断是否更改成功或者是你更改的条数?
     有时候update不成功,但是他也不一定会catch到Exception,你想修改的效果没有达到,但程序没任何异常咋办?
    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);   
            }   
        }   
    }   
}  

46 楼 crazy.j 2010-05-19  
楼主的这玩意有个比较致命的问题,在DAO层就关闭了数据库连接,那么Manager中无法进行比较复杂的事务控制,结合一下Spring可能会有更好的方式。
1年多前的一个项目我也做过跟楼主类似的东西,但有个前提,那个项目禁止使用Hibernate,我提过多次建议都无效,最后没辙为了节省一下简单增删改查的代码量写了几个类。

1.Entity:所有实体实现这个接口。
public interface Entity<IDClass extends java.io.Serializable> {

	public IDClass getId();
	
	public void setId(IDClass id);
}


2.GenericDao:所有Dao实现这个接口。
public interface GenericDao<E extends Entity> {
	
	/**
	 * 保存一个实体
	 * @param entity 实体对象
	 */
	public void save(E entity);
	
	/**
	 * 删除一个实体
	 * @param id 主键
	 */
	public void delete(Serializable id);
	
	/**
	 * 删除一个实体
	 * @param entity 实体对象
	 */
	public void delete(E entity);
	
	/**
	 * 批量删除实体
	 * @param entitiesId 实体编号数组
	 */
	public void deleteBatchByEntitiesId(Object[] entitiesId);
	
	/**
	 * 批量删除实体
	 * @param entities
	 */
	public void deleteBatchByEntities(List<E> entities);
	
	/**
	 * 查询所有
	 * @param page 分页对象
	 * @param sorts 排序对象
	 * @return 查询结果
	 */
	public List getAll(Page page, Sort... sorts);
	
	/**
	 * 按页号和每页记录数查询所有
	 * @param pageIndex 页号
	 * @param pageSize 每页记录数
	 * @param sorts 排序
	 * @return 查询结果
	 */
	public List getAll(Integer pageSize, Integer pageIndex, Sort... sorts);
	
	/**
	 * 通过主键查询
	 * @param id 主键
	 * @return 实体对象
	 */
	public E get(Serializable id);
	
	/**
	 * 按页号和每页记录数查询结果
	 * @param ql 查询语句
	 * @param pageIndex 页号
	 * @param pageSize 每页记录数
	 * @param args 查询语句参数
	 * @return 查询结果
	 */
	public List query(String ql, Integer pageSize, Integer pageIndex, Object ... args);
	
	/**
	 * 通过查询语句查询
	 * @param ql 查询语句
	 * @param page 分页对象
	 * @param args 参数
	 * @return 查询结果
	 */
	public List query(String ql, Page page, Object... args);
	
	/**
	 * 通过查询语句查询,无分页操作
	 * @param ql 查询语句
	 * @param args 参数
	 * @return 查询结果
	 */
	public List query(String ql, Object... args);
	
	/**
	 * 执行更新
	 * @param ql 执行更新
	 * @param args ql参数
	 * @return 更新所影响的行数
	 */
	public int update(String ql, Object... args);
	
	/**
	 * 计算记录数
	 * @param ql 查询语句
	 * @param args 查询条件参数
	 * @return 符合条件的记录数
	 */
	public int count(String ql, Object... args);

}


3.EntityMapper:实体类属性到数据库表字段映射。
public class EntityMapper {

	// 类名
	private Class entityClass;
	
	// 表明
	private String tableName;
	
	// 列名为map键对应的字段名
	private Map<String, String> columnMapField = new HashMap<String, String>();
	
	// 字段名为map建对应的列名
	private Map<String, String> fieldMapColumn = new HashMap<String, String>();
	
	// get方法名称
	private Map<String, String> gettersName = new HashMap<String, String>();
	
	// set方法名称
	private Map<String, String> settersName = new HashMap<String, String>();
	
	// 被映射的字段个数
	private Integer mappedColumnCount;
	
	// 数据库字段
	private String[] columns;
	
	// 类字段
	private String[] fields;
	
	// 类字段类型
	private Class[] fieldsType;
	
	// 插入语句
	private String insert;
	
	// 删除语句
	private String delete;
	
	// 更新语句
	private String update;
	
	// 查询语句
	private String select;
	
	// 查询所有语句
	private String all;
	
	// 匹配大写字母的正则
	private static Pattern p = Pattern.compile("(_|[A-Z]+)");
	
	// 日志
	private static Log log = LogFactory.getLog(EntityMapper.class);
	
	protected static final Map<String, String> RS_METHOD_NAME;
	
	static{
		RS_METHOD_NAME = new HashMap<String, String>();
		RS_METHOD_NAME.put("byte", "getByte");
		RS_METHOD_NAME.put("Byte", "getByte");
		RS_METHOD_NAME.put("short", "getShort");
		RS_METHOD_NAME.put("Short", "getShort");
		RS_METHOD_NAME.put("int", "getInt");
		RS_METHOD_NAME.put("Integer", "getInt");
		RS_METHOD_NAME.put("long", "getLong");
		RS_METHOD_NAME.put("Long", "getLong");
		RS_METHOD_NAME.put("float", "getFloat");
		RS_METHOD_NAME.put("Float", "getFloat");
		RS_METHOD_NAME.put("double", "getDouble");
		RS_METHOD_NAME.put("Double", "getDouble");
		RS_METHOD_NAME.put("Date", "getTimestamp");
		RS_METHOD_NAME.put("boolean", "getBoolean");
		RS_METHOD_NAME.put("Boolean", "getBoolean");
		RS_METHOD_NAME.put("String", "getString");
	}
	
	/**
	 * 构造方法初始化类与数据库表的映射关系
	 * @param className 类名
	 */
	public EntityMapper(Class entityClass) throws SQLException{

		this.entityClass = entityClass;
		this.tableName = convertName(entityClass.getSimpleName());
		
		// 获得该实体类中所有字段名并按规则转换成数据库字段名装入到Map中
		Field[] fields = entityClass.getDeclaredFields();
		Map<String, String> fieldsMap = new HashMap<String, String>();
		for(int i = 0; i < fields.length; i++){
			fieldsMap.put(this.convertName(fields[i].getName()), fields[i].getName());
		}
		
		Connection conn = null;
		ResultSet rs = null;
		
		try {
			conn = ((ConnectionProvider)Platform.getInstance().getBean("ConnectionProvider")).getConnection();
			//  获得数据库中该表所有字段的元数据
			DatabaseMetaData meta = conn.getMetaData();
			rs = meta.getColumns(null, null, this.tableName, null);
			if(log.isDebugEnabled()){
				log.debug(entityClass.getName() + " -> " + this.tableName);
			}
			
			List<String> columnsName = new ArrayList<String>();
			List<String> fieldsName = new ArrayList<String>();
			List<Class> fieldsType = new ArrayList<Class>();
			
			String columnName = null;
			String fieldName = null;
			
			// 循环将数据库中与实体类中公有的字段放入Map
			while(rs.next()){
				columnName = rs.getString(4);
				if((fieldName = fieldsMap.get(columnName)) != null){
					if(log.isDebugEnabled()){
						log.debug(entityClass.getName() + "." + fieldName + " -> " + this.tableName + "." + columnName);
					}
					this.columnMapField.put(columnName, fieldName);
					this.fieldMapColumn.put(fieldName, columnName);
					this.gettersName.put(fieldName, this.convertName("get", fieldName));
					this.settersName.put(fieldName, this.convertName("set", fieldName));
					columnsName.add(columnName);
					fieldsName.add(fieldName);
					fieldsType.add(this.entityClass.getDeclaredField(fieldName).getType());
				}
			}
			this.columns = (String[])columnsName.toArray(new String[columnsName.size()]);
			this.fields = (String[])fieldsName.toArray(new String[fieldsName.size()]);
			this.fieldsType = (Class[])fieldsType.toArray(new Class[fieldsType.size()]);
			this.mappedColumnCount = this.columns.length;
			
			// 初始化CRUD语句
			this.createInsert();
			this.createDelete();
			this.createUpdate();
			this.createSelect();
		} catch(Exception ex) {
			log.error("初始化实体映射类失败...",ex);
		} finally {
			try{
				if(rs != null){
					rs.close();
					conn.close();
				}
			}catch(Exception ex){
				log.error(ex);
			}
		}
	}
	
	/**
	 * 通过结果集创建实体类
	 * @param rs 结果集
	 * @return 实体类
	 */
	public Entity createFromResultSet(ResultSet rs){
		try{
			Entity entity = null;
			if(rs.next()){
				entity = (Entity)this.entityClass.newInstance();
				for(int i = 0; i < fields.length; i++){
					if(log.isDebugEnabled()){
						Object value = rs.getClass().getMethod(RS_METHOD_NAME.get(fieldsType[i].getSimpleName()), String.class).invoke(rs, columns[i]);
						log.debug(this.entityClass.getName() + "." + fields[i] + " -> " + value + " : " + (value == null ? "null" : value.getClass().getName()));
					}
					this.entityClass.getMethod(this.setterName(fields[i]), fieldsType[i]).invoke(entity, rs.getClass().getMethod(RS_METHOD_NAME.get(fieldsType[i].getSimpleName()), String.class).invoke(rs, columns[i]));
				}
			}
			return entity;
		}catch(Exception ex){
			log.warn("通过结果集创建实体类" + this.entityClass.getName() + "对象时发生错误!");
			return null;
		}
	}
	
	/**
	 * 将字段名转换为方法名
	 * @param prefix 前缀
	 * @param fieldName 字段名
	 * @return 方法名
	 */
	private String convertName(String prefix, String fieldName){
		char[] tmp = fieldName.toCharArray();
		tmp[0] = (tmp[0] + "").toUpperCase().charAt(0);
		return prefix + new String(tmp);
	}
	
	/**
	 * 将Java命名规则转换成数据库中的命名规则
	 * @param name Java命名
	 * @return 数据库命名
	 */
	private String convertName(String name){
		Matcher m = p.matcher(name);
		StringBuffer t = new StringBuffer();
		while(m.find()){
			m.appendReplacement(t, "_$1");
		}
		m.appendTail(t);
		
		//类名一般第一个字母大写,所以会多一个'_'符号,如果有符号则需要删除掉
		if(t.charAt(0) == '_'){
			t.deleteCharAt(0);
		}
		return t.toString().toUpperCase().replaceAll("_+", "_");
	}
	
	/**
	 * 初始化插入语句
	 */
	private void createInsert(){
		StringBuffer insert = new StringBuffer("insert into ");
		StringBuffer values = new StringBuffer("values(");
		insert.append(this.tableName);
		insert.append("(");
		for(int i = 0; i < this.columns.length; i++){
			insert.append(this.columns[i]);
			insert.append(", ");
			values.append("?, ");
		}
		insert.delete(insert.length() - 2, insert.length());
		values.delete(values.length() - 2, values.length());
		this.insert = insert.append(") ").append(values).append(")").toString();
		log.debug(this.entityClass.getName() + " insert SQL -> " + this.insert);
	}
	
	/**
	 * 初始化删除语句
	 */
	private void createDelete(){
		StringBuilder delete = new StringBuilder("delete from ");
		delete.append(this.tableName);
		delete.append(" t where t.ID = ?");
		this.delete = delete.toString();
		log.debug(this.entityClass.getName() + " delete SQL -> " + this.delete);
	}
	
	/**
	 * 初始化更新语句
	 */
	private void createUpdate(){
		StringBuilder update = new StringBuilder("update ");
		update.append(this.tableName);
		update.append(" t set ");
		for(int i = 0; i < this.columns.length; i++){
			if(this.columns[i].equals("ID")){
				continue;
			}
			update.append("t.");
			update.append(this.columns[i]);
			update.append(" = ?, ");
		}
		update.deleteCharAt(update.length() - 2);
		update.append("where t.ID = ?");
		this.update = update.toString();
		log.debug(this.entityClass.getName() + " update SQL -> " + this.update);
	}
	
	/**
	 * 创建查询语句
	 */
	private void createSelect(){
		StringBuilder select = new StringBuilder("select ");
		for(int i = 0; i < this.columns.length; i++){
			select.append("t.");
			select.append(this.columns[i]);
			select.append(", ");
		}
		select.deleteCharAt(select.length() - 2);
		select.append(" from ");
		select.append(this.tableName);
		select.append(" t");
		this.all = select.toString();
		select.append(" where t.ID = ?");
		this.select = select.toString();
		log.debug(this.entityClass.getName() + " all SQL -> " + this.all);
		log.debug(this.entityClass.getName() + " select SQL -> " + this.select);
	}
	
	/**
	 * 通过类字段名取得get方法
	 * @param field 字段名 
	 * @return get方法名
	 */
	public String getterName(String field){
		return this.gettersName.get(field);
	}
	
	/**
	 * 通过类字段名取得set方法名
	 * @param field 字段名
	 * @return set方法名
	 */
	public String setterName(String field){
		return this.settersName.get(field);
	}
	
	/**
	 * 通过类字段名取得数据库列明
	 * @param field 字段名
	 * @return 列名
	 */
	public String getColumn(String field){
		return this.fieldMapColumn.get(field);
	}
	
	/**
	 * 通过数据库列明取得类字段名
	 * @param column 列明
	 * @return 字段名
	 */
	public String getField(String column){
		return this.columnMapField.get(column);
	}

	/**
	 * @return the tableName
	 */
	public String getTableName() {
		return tableName;
	}

	/**
	 * @return the entityClass
	 */
	public Class getEntityClass() {
		return entityClass;
	}

	/**
	 * @return the delete
	 */
	public String getDelete() {
		return delete;
	}

	/**
	 * @return the insert
	 */
	public String getInsert() {
		return insert;
	}

	/**
	 * @return the select
	 */
	public String getSelect() {
		return select;
	}

	/**
	 * @return the update
	 */
	public String getUpdate() {
		return update;
	}

	/**
	 * @return the columns
	 */
	public String[] getColumns() {
		return columns;
	}

	/**
	 * @return the fields
	 */
	public String[] getFields() {
		return fields;
	}

	/**
	 * @return the mappedColumnCount
	 */
	public Integer getMappedColumnCount() {
		return mappedColumnCount;
	}

	/**
	 * @return the all
	 */
	public String getAll() {
		return all;
	}

	/**
	 * @return the fieldsType
	 */
	public Class[] getFieldsType() {
		return fieldsType;
	}
}


4.GenericDaoDefault:简单扩展了一下Spring的JdbcDaoSupport,统一处理了几个比较令人厌恶的琐碎代码。
public class GenericDaoDefault<E extends Entity> extends JdbcDaoSupport implements GenericDao<E> {

	// 泛型类型
	protected Class entityClass;
	
	// 实体描述类
	private EntityMapper entity;
	
	// 删除order by字句使用的正则表达式
	private static Pattern removeOrderByPattern = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);

	// 分页处理器
	private PaginationProcessor paginationProcessor;
	
	// 主键生成器
	private IDGenerator iDGenerator;

	
	/**
	 * 构造方法
	 */
	public GenericDaoDefault(){
		try{
			this.entityClass = GenericUtils.getGenericClass(this.getClass());
			if(this.entityClass != null){
				this.entity = new EntityMapper(this.entityClass);
			}
		}catch(Exception ex){
			logger.error("初始化" + this.getClass() + "失败", ex);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#executeUpdate(java.lang.String, java.lang.Object[])
	 */
	public int update(String ql, Object... args) {
		if(logger.isDebugEnabled()){
			logger.debug("Update SQL: \"" + ql + "\"");
		}
		return this.getJdbcTemplate().update(ql, args);
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#delete(java.io.Serializable)
	 */
	public void delete(Serializable id) {
		this.update(this.entity.getDelete(), id);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#delete(com.funstool.platform.common.dao.Entity)
	 */
	public void delete(E entity) {
		this.delete(entity.getId());
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#get(java.io.Serializable)
	 */
	public E get(Serializable id) {
		try{
			Connection conn = super.getConnection();
			if(logger.isDebugEnabled()){
				logger.debug("Select SQL: \"" + this.entity.getSelect() + "\"");
			}
			PreparedStatement ps = conn.prepareStatement(this.entity.getSelect());
			ps.setObject(1, id);
			ResultSet rs = ps.executeQuery();
			return (E)this.entity.createFromResultSet(rs);
		} catch(SQLException ex) {
			logger.warn("获取实体错误", ex);
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#get(java.lang.String, com.funstool.platform.common.dao.Page, java.lang.Object[])
	 */
	public List query(String ql, Page page, Object... args) {
		ql = (String)this.paginationProcessor.analyse(ql, page);
		if(logger.isDebugEnabled()){
			logger.debug("Query SQL: \"" + ql + "\"");
		}
		page.setRecord(super.getJdbcTemplate().queryForList(ql, args));
		return page.getRecord();
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#get(java.lang.String, java.lang.Object[])
	 */
	public List query(String ql, Object... args) {
		if(logger.isDebugEnabled()){
			logger.debug("Query SQL: \"" + ql + "\"");
		}
		return super.getJdbcTemplate().queryForList(ql, args);
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#getAll(com.funstool.platform.common.dao.Page, com.funstool.platform.common.dao.Sort[])
	 */
	public List getAll(Page page, Sort... sorts) {
		String sql = null;
		if(sorts != null && sorts.length > 0){
			StringBuilder s = new StringBuilder(this.entity.getAll());
			s.append(" order by ");
			for(int i = 0; i < sorts.length; i++){
				if(sorts[i] == null){
					continue;
				}
				s.append(sorts[i].toString());
				s.append(", ");
			}
			s.delete(s.length() - 2, s.length());
			sql = (String)this.paginationProcessor.analyse(s.toString(), page);
			if(logger.isDebugEnabled()){
				logger.debug("Select SQL: \"" + sql + "\"");
			}
			page.setRecord(super.getJdbcTemplate().queryForList(sql));
			
			return page.getRecord();
		} else {
			sql = (String)this.paginationProcessor.analyse(this.entity.getAll(), page);
			if(logger.isDebugEnabled()){
				logger.debug("Select SQL: \"" + sql + "\"");
			}
			page.setRecord(super.getJdbcTemplate().queryForList(sql));
			return page.getRecord();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#save(java.lang.Object)
	 */
	public void save(E entity) {
		String[] fields = this.entity.getFields();
		Object[] values = new Object[fields.length];
		try{
			if(entity.getId() == null){
				entity.setId(iDGenerator.generate(super.getConnection()));
				for(int i = 0; i < fields.length; i++){
					values[i] = this.entityClass.getMethod(this.entity.getterName(fields[i])).invoke(entity);
				}
				this.update(this.entity.getInsert(), values);
			} else {
				int idx = 0;
				for(int i = 0; i < fields.length; i++){
					if(fields[i].equals("id")){
						continue;
					}
					values[idx++] = this.entityClass.getMethod(this.entity.getterName(fields[i])).invoke(entity);
				}
				values[values.length - 1] = entity.getId();
				this.update(this.entity.getUpdate(), values);
			}
		} catch(Exception ex) {
			logger.warn("保存实体错误", ex);
			throw new RuntimeException(ex);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#count(java.lang.String, java.lang.Object[])
	 */
	public int count(String ql, Object... args) {
		String sql = "select count(*) " + removeSelect(this.removeOrderBy(ql));
		if(logger.isDebugEnabled()){
			logger.debug("Count SQL: \"" + sql + "\"");
		}
		return super.getJdbcTemplate().queryForInt(sql, args);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#deleteBatchByEntitiesId(java.util.List)
	 */
	public void deleteBatchByEntitiesId(Object[] entitiesId) {
		StringBuilder str = new StringBuilder("delete from ");
		str.append(this.entity.getTableName());
		str.append(" t where t.id in (");
		for(int i = 0; i < entitiesId.length; i++){
			str.append("?,");
		}
		str.deleteCharAt(str.length() - 1);
		str.append(")");
		this.update(str.toString(), entitiesId);
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#deleteBatchByEntities(com.funstool.platform.common.dao.EntityListWrapper)
	 */
	public void deleteBatchByEntities(List<E> entities) {
		StringBuilder str = new StringBuilder("delete from ");
		str.append(this.entity.getTableName());
		str.append(" t where t.id in (");
		for(int i = 0; i < entities.size(); i++){
			str.append("?,");
		}
		str.deleteCharAt(str.length() - 1);
		str.append(")");
		if(!(entities instanceof EntityListWrapper)){
			entities = new EntityListWrapper(entities);
		}
		this.update(str.toString(), ((EntityListWrapper)entities).getEntitiesId());
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#getAll(java.lang.Integer, java.lang.Integer, com.funstool.platform.common.dao.Sort[])
	 */
	public List getAll(Integer pageSize, Integer pageIndex, Sort... sorts) {
		Page page = new Page(pageSize, pageIndex, false);
		return this.getAll(page, sorts);
	}

	/*
	 * (non-Javadoc)
	 * @see com.funstool.platform.common.dao.GenericDao#query(java.lang.String, java.lang.Integer, java.lang.Integer, java.lang.Object[])
	 */
	public List query(String ql, Integer pageSize, Integer pageIndex, Object... args) {
		Page page = new Page(pageSize, pageIndex, false);
		return this.query(ql, page, args);
	}
	
	/**
	 * 删除ql语句中的order by字句
	 * @param ql 查询语句
	 * @return 删除后的查询语句
	 */
	private String removeOrderBy(String ql){
		if(ql != null && !"".equals(ql)){
		    Matcher m = removeOrderByPattern.matcher(ql);
		    StringBuffer sb = new StringBuffer();
		    while (m.find()) {
		      m.appendReplacement(sb, "");
		    }
		    m.appendTail(sb);
		    return sb.toString();
		}
		return "";
	}
	
	/**
	 * 去除ql语句中的select子句
	 * @param ql 查询语句
	 * @return 删除后的语句
	 */
	private String removeSelect(String ql) {
		Assert.hasText(ql);
		int beginPos = ql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " sql : " + ql + " must has a keyword 'from'");
		return ql.substring(beginPos);
	}

	public void setPaginationProcessor(PaginationProcessor paginationProcessor) {
		this.paginationProcessor = paginationProcessor;
	}

	public void setIDGenerator(IDGenerator idGen) {
		this.iDGenerator = idGen;
	}

}
45 楼 lolocomee 2010-05-17  
请发个org.apache.commons.dbcp包给我,谢谢。
我用jdk1.5的,上apache下载的文件出错啊。
邮箱:380546033@qq.com
44 楼 xiao-qiang163 2010-04-19  
基于接口的模板回调,  代码比较精典!  我以前就用这个方法给项目搭过框架的!,但是因为很多人不理解这些回调,故最终放弃了这种方式
43 楼 C_J 2009-08-18  
好像代码上有可以改进点地方.如下

1,mapRow可以用reflect做个通用的接口

  public Object mapRow(ResultSet rs,String className)
{
   ....
}

2,加上线程同步和final:

private final Object lock; 
 public void update(String sql, final Object[] args, final boolean isGeneralKey)   
            throws DaoException {   
 synchronized (lock) {
...
}
}
42 楼 C_J 2009-08-16  

一点拙见:

1,LZ的思路我觉得挺明确的,也比较符合大众的口味。

2,澄清下一些历史。
gaojiewyh 写道
的确和spring相似,
但是spring的依赖注入和策略模式不是很相似嘛,提出控制反转后又提出依赖注入,诸多的概念不感觉重复吗?我写代码只是想从中获得提高


IoC 的概念是Michael Mattson在1996提出来的,Martin Fowler 把IOC就称为DI设计模式,后来IoC鼻祖之一 Stefano Mazzocchi 却指出Martin Fowler这个忽悠了大家,仅仅是从字面上理解。

我觉得比较好的理解是:软件的组装部署和配置完全是由非业务逻辑模块反过来主动控制业务逻辑模块来安排。

详细的内容,见BLOG:http://c-j.iteye.com/admin/blogs/443505

而关于这点思想,从LZ的代码上来看,我感觉仅仅体现在了如下几行:

try {  
            CourseDAO courseDao = (CourseDAO) DaoFactory.getInstance().getDAO(  
                    "courseDao");  
        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  



3,Spring我觉得IOC只是他的基础,这个也是很简单的东西,精髓,但Johnson不仅仅只写出了IOC,也同时提供了transaction,并保证了线程安全(据说是用ThreadLocal保证的)。
    所以,至少我在LZ的代码里好像没看到保证线程安全的机制。我觉得LZ可以加进去,保证不发生死锁现象。


最后,谢谢LZ提供的DBCP的信息。
41 楼 tangbo530 2009-08-12  
你不觉得在DAO层关闭数据库连接不是那么妥当么
40 楼 huidian 2009-07-29  
不明白。太罗嗦了。就单纯IOC容器而言,spring可没这么简单。
39 楼 eyeshot 2009-07-29  
代码很清晰,对spring的原理理解很有帮忙!
38 楼 fish4j 2009-07-29  
呵呵,LZ中创的?
37 楼 uptorun 2009-07-28  
很不错的Dao设计, 但是建议在获取连接的Util类中把Connection 对象邦定到线程中更加合适,这样可以在业务层对事物进行控制,如果模仿spring的话,可以再搞个TransactionManager , 在事务结束时自动释放资源,也就不用写Release了,
36 楼 yinchunjian 2009-07-27  
whg333 写道
学习了...

gaojiewyh 写道
但是spring的依赖注入和策略模式不是很相似嘛提出控制反转后又提出依赖注入,诸多的概念不感觉重复吗?我写代码只是想从中获得提高


Spring的依赖注入我觉得更像静态工厂方法吧(没看过Spring源码),也是去读取配置文件...

控制反转(IoC)是一种理论,依赖注入(DI)是实现IoC的一种方式而已,并不重复...



学习学习
35 楼 TracyKelvin 2009-07-27  
效率怎么样呢?
34 楼 grandboy 2009-07-26  
whg333 写道
nishijia 写道
TO whg333:

autoCommit 有意义吗? 通常会把connection autoCommit设置为false,等做完一段操作后 connection。commit() 这不就实现了transcation事务么


对,我说的就是这个,我说可以设置autoCommit-》setAutoCommit为true或 false,为false的话当然只能自己写事务代码了...





true的时候也可以做为readonly的transaction来处理啊。思路不错,我现在也重复发明了一个轮子。至少有什么问题,随时都可以自己改了,不用读那么多别人的代码,如果理解不透的话,改了还会影响其他地方。我觉得没有必要反对一切重复发明, 在重复发明的同时也一定是有创新的。
33 楼 stevensinclair 2009-07-25  
xdwinner 写道
建议DaoException继承RuntimeException


为什么?

难道是看到了DataAccessException?

我是主张extends Exception。

讨论下
32 楼 whg333 2009-07-23  
nishijia 写道
TO whg333:

autoCommit 有意义吗? 通常会把connection autoCommit设置为false,等做完一段操作后 connection。commit() 这不就实现了transcation事务么


对,我说的就是这个,我说可以设置autoCommit-》setAutoCommit为true或 false,为false的话当然只能自己写事务代码了...



相关推荐

Global site tag (gtag.js) - Google Analytics