`

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的话当然只能自己写事务代码了...



相关推荐

    学习JAVA3个月写了个东西,请同志们指正,SSH源代码

    8. **DAO设计模式**:创建数据访问对象,隔离业务逻辑与数据库操作。 9. **Service层**:编写业务服务层,封装业务逻辑。 10. **JSP和EL**:使用JSP页面展示数据,利用EL表达式简化页面中的Java代码。 11. **JSTL**...

    JAVA核心技术

    在软件设计里到处都是模式,框架。有次朋友问什么是模式?我也在学习中,就我的学习经验,给出以下小结。...5.其实本文的目的在于“抛砖引玉”,希望各路高手请你们把各种框架的特点和出处罗列一下 ,供大家参考,选

    JavaEye论坛热点_-_2008年11月_-_总第6期

    - DAO(数据访问对象)模式是一种常见的设计模式,用于封装对数据库的操作。作者分享了他对通用DAO的理解,邀请其他开发者进行讨论和指正。 6. **阅读源代码的技巧** - 阅读和理解源代码是开发者的基本技能。文章...

    健身俱乐部Web网站-JSP+Servlet+Hibernate+jQuery+Ajax

    运用了一些基本设计模式,系统可扩展性还是非常良好的。 具体安装运行方法见readme.txt文件。附带了建表sql文件。最好先看下文档目录下的架构文档、需求概要文档以及几个类图,有助于快速理解整个项目。下面是...

    【人工智能产业链分析】2018-2024年中国各地区AI产业集中度与财务状况及产业话语权研究

    中国全国AI人工智能产业链关系2018-2024集中度指标按地区分销售财务情况产业话语权指标等 数据来源:基于国内各部委公布数据及各地统计数据整理,或相关省、市数据 数据范围:全国、或各省份地市的统计数据 数据期间:(参见其文件名的标识表达了数据年度、或月度日度期间) 主要指标: 包含的数据文件有: 中国全国AI人工智能产业链产业基本情况2020-2024.zip 中国全国AI人工智能产业链产业上市公司按地区分销售情况统计表2020-2024.zip 中国全国AI人工智能产业链产业上市公司财务情况统计表2020-2024.zip 中国全国AI人工智能产业链产业上市公司销售情况统计表2020-2024.zip 中国全国AI人工智能产业链产业政策信息表2020-2024.zip 中国全国AI人工智能产业链关系表2024.zip 中国全国AI人工智能产业链集中度指标表2020-2024.zip 中国全国AI人工智能产业链名单2024.zip 中国全国AI人工智能产业链企业基本信息表2018-202501.zip 中国全国AI人工智能产业链企业名单2024.zip 中国全国AI人工智能产业链上市公司产业话语权指标表2020-2024.zip (近百MB数据的网盘链接)

    DOS游戏 - RPG - 侠客英雄传3

    注意事项: 1、运行游戏方法 :双击 “侠客英雄传3.bat” 运行。 2、更换光碟提示: 使用 CTRL+F4 组合键后 按空格即可跳过。

    电力电子领域两相交错并联Buck/Boost变换器的Matlab/Simulink仿真及控制策略分析

    内容概要:本文详细介绍了两相交错并联Buck/Boost变换器的建模与仿真,重点探讨了三种不同的控制方式及其性能表现。首先,文章描述了模型的基本架构,包括两个桥臂共用直流母线,MOSFET采用理想的双向开关,电流传感器安装在电感支路上。接着,分别讨论了开环控制、电压单环控制和电压电流双闭环控制的具体实现方法和优缺点。开环模式适用于观察硬件参数的影响,电压单环控制虽然稳定但在负载突变时响应较慢,而双闭环控制能够显著提高系统的动态响应和电流均流效果。此外,文章还分享了一些实用的仿真技巧,如正确设置死区时间和仿真步长,确保仿真的准确性和效率。 适合人群:电力电子工程师、科研人员、高校师生等对DC-DC变换器设计和仿真感兴趣的读者。 使用场景及目标:①研究两相交错并联Buck/Boost变换器的工作原理和控制策略;②优化变换器的设计参数,提高系统的稳定性和效率;③掌握Matlab/Simulink进行复杂电力电子系统仿真的方法和技术。 其他说明:文中提供了详细的代码片段和仿真波形,帮助读者更好地理解和应用相关理论和技术。

    ffmpeg7.0 + sdl3.0 播放音频

    ffmpeg7.0 + sdl3.0 播放音频

    基于龙贝格观测器的PMSM无传感器控制技术解析与应用

    内容概要:本文深入探讨了基于龙贝格观测器的永磁同步电机(PMSM)无传感器控制技术。首先介绍了龙贝格观测器相较于传统滑模观测器(SMO)的优势,特别是在减少系统抖振方面表现突出。接着详细解释了龙贝格观测器的工作原理,包括状态预测、误差补偿以及角度解算三大核心步骤,并提供了具体的代码实现。文中还讨论了实际工程应用中的挑战,如参数选择、噪声处理等问题,并给出了相应的解决方案。此外,文章通过实验数据展示了龙贝格观测器在不同工况下的性能优势,尤其是在高速和低速情况下的稳定性和响应速度。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是关注无传感器控制领域的工程师。 使用场景及目标:适用于希望提升PMSM无传感器控制系统的稳定性、精确度的研发团队。主要目标是在保持高性能的同时降低系统复杂度,提高产品竞争力。 其他说明:文中不仅分享了理论知识和技术细节,还提供了大量实用的经验技巧,帮助读者更好地理解和应用龙贝格观测器进行实际项目开发。

    PMSM伺服系统转动惯量与阻尼系数在线辨识方法及仿真研究

    内容概要:本文深入探讨了永磁同步电机(PMSM)伺服系统的转动惯量和阻尼系数的在线辨识方法。文中介绍了两种主要的辨识方程:一种用于空载工况,另一种用于负载工况。通过详细的数学推导和Python、C、MATLAB代码示例,展示了如何在不同工况下精准辨识这些参数。此外,还讨论了1.5拍延时补偿、全电压前馈补偿和相电压重构等关键技术,以提高辨识精度和系统稳定性。仿真结果显示,在空载和负载突变情况下,辨识误差分别低于0.8%和2.3%。 适合人群:从事电机控制、自动化控制领域的研究人员和技术人员,尤其是对PMSM伺服系统感兴趣的工程师。 使用场景及目标:适用于需要在线辨识PMSM伺服系统转动惯量和阻尼系数的应用场合,如工业机器人、数控机床等。目标是在不影响正常生产的情况下,实时监测和调整电机参数,提升系统性能。 其他说明:本文不仅提供了理论推导和算法实现,还给出了具体的代码示例和仿真结果,便于读者理解和应用。同时,文中提到的技术可以作为其他类似算法验证的良好参考。

    (源码)基于Arduino的精确计时与PWM控制系统.zip

    # 基于Arduino的精确计时与PWM控制系统 ## 项目简介 本项目基于Arduino的TimerOne库,该库是Arduino平台上用于精确计时和PWM控制的开源库。主要面向Arduino板上的ATmega系列微控制器,可实现设置定时器、产生PWM信号、定时中断等功能,用于精确控制时间和电机速度。 ## 项目的主要特性和功能 1. 初始化定时器,设置初始参数。 2. 根据用户指定微秒数设置定时器周期。 3. 设定PWM输出的占空比,控制PWM输出大小。 4. 启动和停止PWM输出。 5. 设定和停止中断服务例行程序。 6. 重新启动和重置定时器。 7. 停止定时器计数。 8. 读取当前定时器的计数值并转换为微秒数。 ## 安装使用步骤 ### 安装 用户已下载项目源码文件后,可通过Arduino IDE的库管理器搜索并安装TimerOne库。 ### 使用 在代码中引入#include <TimerOne.h>,即可使用上述功能。

    weixin242基于微信小程序的外卖点餐系统设计与实现ssm(文档+源码)_kaic

    weixin242基于微信小程序的外卖点餐系统设计与实现ssm(文档+源码)_kaic

    (源码)基于Arduino的Wemos Mqtt Alarm Panel项目.zip

    # 基于Arduino的Wemos Mqtt Alarm Panel项目 ## 项目简介 本项目是一个基于Arduino平台的开源智能报警面板项目,命名为“Wemos Mqtt Alarm Panel”。该项目允许用户通过简单的MQTT操作来触发和控制报警系统。主要面向需要低成本、易于部署的智能家居或小型商业场所报警系统。项目仍在开发阶段,但已经具备基本功能并可供使用。 ## 项目的主要特性和功能 1. 低成本硬件需求主要使用Wemos D1 Mini或其他兼容的微控制器,以及Lolin 2.4英寸TFT显示屏。整体硬件成本较低,易于获取和部署。 2. 基于MQTT通信协议允许报警系统与MQTT服务器进行通信,实现远程控制和状态报告功能。 3. 界面友好采用直观的图形界面,支持触摸操作,方便用户进行交互。 4. 校准功能提供校准界面,确保触摸操作的准确性。 5. 可扩展性支持自定义报警事件和动作,允许用户根据需求进行个性化设置。 ## 安装使用步骤

    MATLAB实现SSA-ESN多输出回归:代码解析与应用实例

    内容概要:本文详细介绍了一个基于MATLAB的SSA-ESN(奇异谱分析-回声状态网络)多输出回归代码。该代码适用于处理复杂的非线性回归问题,具有多输出支持、友好的数据格式、丰富的可视化效果以及全面的评价指标等特点。文中不仅提供了详细的代码解析,还给出了具体的操作步骤和注意事项,帮助初学者快速理解和应用这一先进的回归方法。主要内容分为数据预处理、模型训练与预测、结果分析与可视化三个部分,涵盖了从数据准备到最终结果呈现的完整流程。 适合人群:对机器学习感兴趣特别是想学习和应用SSA-ESN进行多输出回归的新手程序员和研究人员。 使用场景及目标:①用于解决多输出的非线性回归问题;②提供一个完整的项目案例,帮助用户理解SSA-ESN的工作机制及其优势;③通过实际操作加深对机器学习理论的理解。 其他说明:代码已调试完毕,可以直接运行,附有详细的中文注释,便于学习和修改。此外,文中还提到了一些常见的错误及解决方案,如数据格式不匹配等问题。

    基于Matlab的模拟射击自动报靶系统:图像处理与计算机视觉技术实现高效报靶

    内容概要:本文详细介绍了一个基于Matlab的模拟射击自动报靶系统的实现方法。该系统利用图像处理技术和计算机视觉技术,通过一系列步骤如图像滤波、图像减影、二值化、噪声滤除、目标矫正、弹孔识别和环值判定,实现了对射击靶纸的自动化处理。此外,文中还介绍了如何使用Matlab的GUIDE工具创建友好的GUI界面,使系统更易于操作。系统不仅提高了报靶的速度和准确性,还在军事训练和民用射击活动中展现出广阔的应用前景。 适合人群:对图像处理、计算机视觉感兴趣的研发人员和技术爱好者,尤其是有一定Matlab基础的用户。 使用场景及目标:适用于射击训练和比赛中,用于快速准确地报靶,提高训练效率和比赛公平性。目标是通过自动化手段减少人工干预,确保报靶结果的客观性和实时性。 其他说明:文中提供了详细的代码示例和优化技巧,帮助读者更好地理解和实现该系统。此外,作者还分享了一些常见问题的解决方案,如光照突变、靶纸反光等问题的应对措施。

    【Docker Compose】高级编排实战指南:涵盖Web全栈、AI/ML、IoT、监控、CI/CD等多领域应用部署摘要 本文

    内容概要:本文深入探讨了 Docker Compose 的高级应用,旨在帮助用户从基础用户成长为能够驾驭复杂系统编排的专家。文章首先介绍了 Docker Compose 的核心高级特性,如 profiles、extends、depends_on、healthcheck、自定义网络、卷管理和环境变量管理。接着,通过 30 多个实战模板,覆盖了 Web 全栈、AI/ML、IoT、监控、CI/CD 等多个领域的复杂场景,展示了如何利用这些特性构建高效、可靠的应用环境。每个模板不仅提供了详细的代码示例,还附有解释要点,帮助读者理解其工作原理和应用场景。 适用人群:具备一定 Docker 基础,希望提升 Docker Compose 使用技能的开发者和运维人员,特别是那些需要管理复杂多服务应用的 DevOps 工程师。 使用场景及目标: 1. **Web 开发**:构建 LEMP、MERN 等全栈应用,简化开发和部署流程。 2. **数据处理**:实现 ETL 流程,结合消息队列和数据库进行高效数据处理。 3. **微服务架构**:使用 API 网关简化微服务入口管理,提升服务发现和路由配置的灵活性。 4. **监控与日志**:搭建 PLG 或 ELK 日志系统,实现日志的收集、存储和可视化。 5. **物联网**:构建 MQTT Broker 和时序数据库,支持 IoT 设备的数据接收和处理。 6. **机器学习**:部署 TensorFlow Serving 或 TorchServe,提供模型服务接口。 7. **CI/CD**:快速搭建 GitLab/Gitea 平台,支持代码托管和持续集成。 8. **安全测试**:使用 OWASP ZAP 对 Web 应用进行自动化或手动的安全扫描。 9. **教育与学习**:部署 Moodle,在线学习管理系统,支持课程创建和

    基于COMSOL的注浆技术仿真:浆液扩散特性与优化方案

    内容概要:本文详细探讨了利用COMSOL软件对注浆技术进行仿真的方法和技术细节。主要内容包括浆液扩散的数学建模、仿真模型的构建(如几何模型、边界条件、初始条件和控制方程)、关键参数(注浆压力、孔间距、地质条件)对浆液扩散的影响分析,以及实际工程应用案例。文中通过具体实例展示了如何通过仿真优化注浆施工参数,提高注浆效率并降低成本。此外,还讨论了倾斜裂隙、孔隙率和渗透率等因素对浆液扩散的具体影响及其应对措施。 适合人群:从事地下工程施工的技术人员、科研人员及高校相关专业师生。 使用场景及目标:①用于优化注浆施工方案,提高注浆效果;②为地下工程建设提供技术支持;③帮助研究人员深入理解浆液扩散机制。 其他说明:文章不仅提供了理论分析,还包括大量具体的代码示例和实践经验分享,有助于读者更好地理解和应用COMSOL仿真技术。

    【信息安全领域】实战项目汇总:涵盖网络渗透测试、Web应用安全加固、企业安全策略制定等多方面内容

    内容概要:本文列举了多个信息安全领域的实战项目示例,涵盖网络渗透测试、Web应用安全加固、企业安全策略制定与实施、恶意软件分析、数据泄露应急响应、物联网设备安全检测、区块链安全审计和云安全防护八大方面。每个项目均明确了具体的目标与步骤,如网络渗透测试通过模拟攻击发现并修复系统漏洞;Web应用安全加固则从代码审查、输入验证、身份验证、数据加密等方面确保应用安全;企业安全策略制定旨在构建全面的信息安全体系;恶意软件分析深入探究其功能与传播机制;数据泄露应急响应项目则聚焦于快速遏制影响、调查原因、恢复系统;物联网设备安全检测保障设备的安全性;区块链安全审计确保系统稳定可靠;云安全防护构建云环境下的安全体系。; 适合人群:信息安全从业人员、网络安全工程师、企业IT管理人员、安全研究人员。; 使用场景及目标:适用于希望深入了解信息安全各细分领域实战操作的专业人士,目标是掌握不同类型安全项目的实施流程与技术要点,提升实际工作中应对安全挑战的能力。; 其他说明:文中提供的项目示例不仅有助于理论学习,更为实际工作提供了具体的指导和参考,帮助相关人员在不同场景下有效开展信息安全工作,确保信息系统的安全性、稳定性和可靠性。

    基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码(毕业设计&课程设计&项目开发)

    基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用 基于matlab实现的博弈方法的电动汽车充电调度策略研究+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用

    基于Matlab的违规限号车牌识别管理系统:从粗定位到精准识别

    内容概要:本文详细介绍了基于Matlab的违规限号车牌识别管理系统的开发过程和技术细节。系统主要分为多个步骤,包括车牌粗定位、灰度化、倾斜矫正、二值化、形态学处理、反色处理、精准定位、字符分割与识别、限号判断、语音播报和违规车牌信息导出。通过这些步骤,系统能够高效地识别并处理违规限号车辆,提升交通管理水平。 适用人群:适用于具有一定编程基础的技术人员,特别是对计算机视觉和数字图像处理感兴趣的开发者。 使用场景及目标:本系统主要用于城市交通管理部门,帮助执法人员快速识别和处理违反限号规定的车辆,提高交通管理的智能化水平。具体应用场景包括但不限于道路监控、停车场管理和临时检查点等。 其他说明:文中提供了大量Matlab代码示例,详细解释了各个步骤的具体实现方法。此外,作者还分享了许多实际开发过程中遇到的问题及其解决方案,有助于读者更好地理解和应用这些技术。

Global site tag (gtag.js) - Google Analytics