手把手教你如何自定义DAO框架(重量级干货)

描述:本篇博客,主要是对于目前实际web项目开发中,对于数据库操作的一些基本方法的封装,通过这样的方式,可以使得开发更加简单,减少代码量,也便于维护和阅读。其中,主要是讲解了三种不同情况的封装方法,都是自身实际开发过程中,进行积累的,当然,还有很多不足之处,但是基本够项目的基本开发。

一:非框架的DAO层封装

其中包含的知识点:

1:连接池

2:数据源

3:反射

4:数据库元数据对象

5:基本的jdbc知识

封装步骤:

(1)导入dbcp数据源包


当然也可以用其他的数据源,比如c3p0,如果想了解更多,可以阅读我的另外一篇文章:

打开连接,查看更多的数据源实现方法

(2)编写properties文件,并且命令为dbcpconfig.properties

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/填写要使用的数据库
username=填写自己的mysql账号
password=填写自己的mysql密码

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

#最大连接数量
maxActive=50

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

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

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


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

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

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

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

(3)编写DBCP工具类

package com.hnu.scw.utils;
import java.awt.image.DataBuffer;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtils {
	private static DataSource ds ;	
	static {
		//将配置文件加载进来
		InputStream in = DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties") ;
		Properties props = new Properties() ;
		try {
			props.load(in) ;
			ds = BasicDataSourceFactory.createDataSource(props) ;
		} catch (Exception e) {
			throw new RuntimeException("服务器忙") ;
		}
	}
	
	//提供获取连接的方法
	public static Connection getConnection(){
		try {
			return ds.getConnection() ;
		} catch (SQLException e) {
			throw new RuntimeException("服务器忙") ;
		}
	}
}

(4)编写基本DAO层方法封装

package com.hnuscw.BaseDao;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.hnu.scw.handler.ResultHandler;
import com.hnu.scw.handler.impl.ResultSetHandlerImpl;
import com.hnu.scw.handler.impl.ResultSetListenerHandlerImpl;
import com.hnu.scw.utils.DBCPUtils;
//自定义框架
public class BaseDao{

	// 执行添改删语句
	public boolean update(String sql, Object... params) {
		// 拿到连接对象
		Connection conn = DBCPUtils.getConnection();
		int t = 0;
		try {
			// 创建预处理命令对象
			PreparedStatement pstmt = conn.prepareStatement(sql);
			// 对?进行赋值
			// 获取ParameterMetaData对象
			ParameterMetaData pmd = pstmt.getParameterMetaData();
			// 拿到?的个数
			int n = pmd.getParameterCount();
			if (n > 0) {
				// sql语句里有?号
				if (params == null || params.length != n) {
					throw new RuntimeException("参数的个数不匹配");
				}
				// 依次给每个?赋值
				for (int i = 0; i < n; i++) {
					pstmt.setObject(i + 1, params[i]);
				}
			}

			t = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close(); // 还回池中了
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return t > 0 ? true : false;
	}

	// 执行查询(返回的结果可能是一个或者多个,或者为null,这个就具体根据返回结果再进行处理即可)
	public Object queryOne(String sql,Class clazz, Object... params) {
		// 拿到连接对象
		Connection conn = DBCPUtils.getConnection();
		try {
			// 创建预处理命令对象
			PreparedStatement pstmt = conn.prepareStatement(sql);
			// 对?进行赋值
			// 获取ParameterMetaData对象
			ParameterMetaData pmd = pstmt.getParameterMetaData();
			// 拿到?的个数
			int n = pmd.getParameterCount();
			if (n > 0) {
				// sql语句里有?号
				if (params == null || params.length != n) {
					throw new RuntimeException("参数的个数不匹配");
				}
				// 依次给每个?赋值
				for (int i = 0; i < n; i++) {
					pstmt.setObject(i + 1, params[i]);
				}
			}

			ResultSet rs = pstmt.executeQuery();
			//返回的结果可能是一个或者多个,或者为null
			return new ResultSetListenerHandlerImpl().handler(rs, clazz) ;
		} catch (SQLException e) {
			throw new RuntimeException() ;
		} finally {
			try {
				conn.close(); // 还回池中了
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

(5)查询返回结果集与实体bean的封装对象--------ResultSetListenerHandlerImpl

package com.hnu.scw.handler.impl;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
//只适用于结果集有多条记录的情况
//对象的属性名和表中的字段名应当一致
public class ResultSetListenerHandlerImpl{
	public Object handler(ResultSet rs, Class clazz) {		
		List<Object> list = new ArrayList<Object>() ;
		//拿到结果集的元数据对象
		try {
			while(rs.next()){
				ResultSetMetaData rsmd = rs.getMetaData() ;
				//拿到公有多少列
				int columnCount = rsmd.getColumnCount() ;
				//先创建对象
				Object obj = clazz.newInstance() ;
				for (int i = 0; i < columnCount; i++) {
					//拿到列名
					String columnName = rsmd.getColumnName(i+1) ;		
					//拿到对象对应的属性
					Field field = clazz.getDeclaredField(columnName) ;
					//设置私有属性可以访问
					field.setAccessible(true) ;
					//拿到此列对应的值
					Object objectValue = rs.getObject(i+1) ;
					//给属性赋值
					field.set(obj, objectValue) ;
				}
				list.add(obj) ;
			}
			return list ;
		} catch (Exception e) {
			throw new RuntimeException() ;
		}
	}

}
上面这个是返回一个List的情况,如果只想是返回单个对象,那么很简单,稍微处理一下就可以用下面这个类来实现:

package com.hnu.scw.handler.impl;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
//只适用于结果集只有一条记录的情况
//对象的属性名和表中的字段名应当一致
public class ResultSetHandlerImpl {
	public Object handler(ResultSet rs, Class clazz) {
		//拿到结果集的元数据对象
		try {
			if(rs.next()){
				ResultSetMetaData rsmd = rs.getMetaData() ;
				//拿到公有多少列
				int columnCount = rsmd.getColumnCount() ;
				//先创建对象
				Object obj = clazz.newInstance() ;
				for (int i = 0; i < columnCount; i++) {
					//拿到列名
					String columnName = rsmd.getColumnName(i+1) ;		
					//拿到对象对应的属性
					Field field = clazz.getDeclaredField(columnName) ;
					//设置私有属性可以访问
					field.setAccessible(true) ;
					//拿到此列对应的值
					Object objectValue = rs.getObject(i+1) ;
					//给属性赋值
					field.set(obj, objectValue) ;
				}
				return obj ;
			}else
				return null ;
		} catch (Exception e) {
			throw new RuntimeException() ;
		}
	}

}

(6)测试实例

package com.hnu.scw.test;
/**
 * create table account   //模拟的一个数据库表
 * (
 * 	   id int primary key,
 * 	   name varchar(30) ,
 * 	   money float
 * )
 */
import java.util.List;
import org.junit.Test;
import com.hnu.scw.DBAssist.BaseDao;
import com.hnu.scw.bean.Account;
//测试自定义框架
public class Test2 {
	// 测试添加
	@Test
	public void test() {
		DBAsssist db = new DBAsssist();
		db.update("insert into account(id,name,money) values(?,?,?)", 2, "乔峰",
				2000);
	}

	// 测试更新
	@Test
	public void test1() {
		DBAsssist db = new DBAsssist();
		db.update("update account set money = money + ? where id = ?", 500, 1);
	}

	// 测试更新
	@Test
	public void test2() {
		DBAsssist db = new DBAsssist();
		db.update("delete from account where id = ?", 1);
	}

	// 测试查询
	@Test
	public void test3() {
		DBAsssist db = new DBAsssist();
		List<Account> list = (List<Account>)db.query("select * from account", Account.class) ;
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
}
对应的一个实体对象bean:

package com.hnu.scw.bean;
import java.io.Serializable;
public class Account implements Serializable{
	private int id ;
	private String name ;
	private float money ;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public float getMoney() {
		return money;
	}
	public void setMoney(float money) {
		this.money = money;
	}
	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money
				+ "]";
	}
}

二:Mybatis的DAO层封装

(1)一般先写接口:

package com.hnu.scw.dao;
import sun.security.provider.VerificationProvider;
public interface DAO {
	/**
	 * 保存对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object save(String str, Object obj) throws Exception;

	/**
	 * 修改对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object update(String str, Object obj) throws Exception;

	/**
	 * 删除对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object delete(String str, Object obj) throws Exception;

	/**
	 * 查找对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object findForObject(String str, Object obj) throws Exception;

	/**
	 * 查找对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object findForList(String str, Object obj) throws Exception;

	/**
	 * 查找对象封装成Map
	 * 
	 * @param s
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object findForMap(String sql, Object obj, String key, String value) throws Exception;
}

(2)再写实现层:

package com.mbfw.dao;

import java.util.List;

import javax.annotation.Resource;

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

@Repository("daoSupport")
public class DaoSupport implements DAO {

	@Resource(name = "sqlSessionTemplate")
	private SqlSessionTemplate sqlSessionTemplate;

	/**
	 * 保存对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object save(String str, Object obj) throws Exception {
		return sqlSessionTemplate.insert(str, obj);
	}

	/**
	 * 批量更新
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object batchSave(String str, List objs) throws Exception {
		return sqlSessionTemplate.insert(str, objs);
	}

	/**
	 * 修改对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object update(String str, Object obj) throws Exception {
		return sqlSessionTemplate.update(str, obj);
	}

	/**
	 * 批量更新
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public void batchUpdate(String str, List objs) throws Exception {
		SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
		// 批量执行器
		SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		try {
			if (objs != null) {
				for (int i = 0, size = objs.size(); i < size; i++) {
					sqlSession.update(str, objs.get(i));
				}
				sqlSession.flushStatements();
				sqlSession.commit();
				sqlSession.clearCache();
			}
		} finally {
			sqlSession.close();
		}
	}

	/**
	 * 批量更新
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object batchDelete(String str, List objs) throws Exception {
		return sqlSessionTemplate.delete(str, objs);
	}

	/**
	 * 删除对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object delete(String str, Object obj) throws Exception {
		return sqlSessionTemplate.delete(str, obj);
	}

	/**
	 * 查找对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object findForObject(String str, Object obj) throws Exception {
		return sqlSessionTemplate.selectOne(str, obj);
	}

	/**
	 * 查找对象
	 * 
	 * @param str
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public Object findForList(String str, Object obj) throws Exception {
		return sqlSessionTemplate.selectList(str, obj);
	}

	public Object findForMap(String str, Object obj, String key, String value) throws Exception {
		return sqlSessionTemplate.selectMap(str, obj, key);
	}
}

三:Hibernate的DAO层

备注:这里就不用接口去编写了,在实际的开发过程中,可以将下面的方法写在一个接口中,然后用该类去实现接口,这样的话就更加面向接口编程了。
package com.hnu.scw.dao;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;

@SuppressWarnings("unchecked")
public class BaseDAO<T> {
    protected static Logger logger = Logger.getLogger(BaseDAO.class);

    //session获取工厂,这个主要是为了能够保证整个里面只存在一个对象,减少消耗
    @Autowired
	private SessionFactory sessionFactory;
    //数据库的数据源
	@Autowired
	private DataSource dataSource;
    /**
     *   泛型类,主要是为了后面的方法返回不同的model类型
     */
	protected Class<T> clz;

	private List list;

	public BaseDAO() {
		clz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
    /**
     *   获取session,主要是用来进行增删改查操作,hibernate都是基于这个
     *   这样的方式还可以改成于单例设计模式,
     */
	protected Session getSession() {
		try {
	        return sessionFactory.getCurrentSession();
        } catch (Exception e) {
	        e.printStackTrace();
        }
		return null;
	}

    /**
     * 获取单个model数据(hql语句)
     * @param hql:hql数据库语句
     * @param paras:参数
     * @return:返回对应的查询结果,如果没有查询到就是返回null
     */
	public T findOne(String hql, Object[] paras) {
		Page page = new Page();
		page.setPage(1);
		page.setPageSize(1);
	    List<T> list = find(hql, paras, page);
	    if (CollectionUtils.isEmpty(list)) {
	        return null;
	    } else {
	        //因为可能list里面存在多个数据条目,但是这里就取第一个,所以就是返回一个了
	        return list.get(0);
	    }
	}

    /**
     * 这是一个find方法的重载
     * 分页处理,获取对应条目的数据内容
     * @param hql:hql查询语句
     * @param paras:参数
     * @param start:从哪里开始查询的索引
     * @param limit:从开始索引的间隔数
     * @return:返回分页获取到的数据条目
     */
	public List<T> find(String hql, Object[] paras, Integer start, Integer limit) {
		Query<T> query = getSession().createQuery(hql);
		if (logger.isDebugEnabled()) {
		    String str = "";
		    str += "hql: " + hql;
            for (int i = 0; i < paras.length; i++) {
                str += ", 参数 " + i + ": " + paras[i];
            }
            logger.debug(str);
        }
		if (paras != null) {
			for (int i = 0; i < paras.length; i++) {
				query.setParameter(i, paras[i]);
			}
		}
		if (start != null && start >= 0 && limit != null && limit >= 0) {
			query.setFirstResult(start);
			query.setMaxResults(limit);
		}
		return query.list();
	}

    /**
     * 获取分页的数据内容
     * @param hql
     * @param paras
     * @param page:page对象,这就是分页对象,里面有总条数和总页数,当前页数等内容
     * @param <K>
     * @return
     */
	public <K> K find2(String hql, Object[] paras, Page page) {
        Query query = getSession().createQuery(hql);

        if (logger.isDebugEnabled()) {
            String str = "";
            str += "hql: " + hql;
            for (int i = 0; i < paras.length; i++) {
                str += ", 参数 " + i + ": " + paras[i];
            }
            logger.debug(str);
        }
        if (paras != null) {
            for (int i = 0; i < paras.length; i++) {
                query.setParameter(i, paras[i]);
            }
        }

        if (page != null) {
            if (StringUtils.isNotBlank(page.getSort())) {
            }
            //获取对应查询语句的数据总条数
            int totalCount = queryTotalCount(hql, paras);
            page.setTotalRecords(totalCount);
            int totalPages = totalCount % page.getPageSize() == 0 ? (totalCount / page.getPageSize()) : (totalCount / page.getPageSize() + 1);
            page.setTotalPages(totalPages);
            query.setFirstResult((page.getPage() - 1) * page.getPageSize());
            query.setMaxResults(page.getPageSize());
        }
        return (K) query.list();
    }

    /**
     * 返回多条数据
     * @param hql:hql数据库语句
     * @param paras:参数
     * @param page:分页处理
     * @return 返回多个数据
     */
	public List<T> find(String hql, Object[] paras, Page page) {
		Query<T> query = getSession().createQuery(hql);
		
		if (logger.isDebugEnabled()) {
		    String str = "";
		    str += "hql: " + hql;
            for (int i = 0; i < paras.length; i++) {
                str += ", 参数 " + i + ": " + paras[i];
            }
            logger.debug(str);
        }
		if (paras != null) {
			for (int i = 0; i < paras.length; i++) {
				query.setParameter(i, paras[i]);
			}
		}

		if (page != null) {
			if (StringUtils.isNotBlank(page.getSort())) {
			}

			int totalCount = queryTotalCount(hql, paras);
			page.setTotalRecords(totalCount);
			int totalPages = totalCount % page.getPageSize() == 0 ? (totalCount / page.getPageSize()) :(totalCount / page.getPageSize() + 1); 
			page.setTotalPages(totalPages);
			query.setFirstResult((page.getPage() - 1) * page.getPageSize());
			query.setMaxResults(page.getPageSize());
		}

		return query.list();
	}

    /**
     * 查询数据的总条数
     * @param hql:查询语句
     * @param paras:查询参数
     * @return:返回符合查询语句的总条数
     */
	public int queryTotalCount(String hql, Object[] paras) {
		int beginPos = hql.toLowerCase().indexOf("from");
		String countHql = "select count(*) " + hql.substring(beginPos);
		Query<Object> query = getSession().createQuery(countHql);
		if (paras != null) {
			for (int i = 0; i < paras.length; i++) {
				query.setParameter(i, paras[i]);
			}
		}
		return Integer.parseInt(query.uniqueResult().toString());
	}

    /**
     * 保存数据
     * @param t:泛型,一般就是用model类
     * @return
     */
	public Long create(T t) {
		return (Long) getSession().save(t);
	}

    /**
     * 采取懒加载获取数据(延迟加载机制,就是只有当使用对象的时候才会真正执行查询操作)
     * @param id:id字段
     * @return 返回对应的数据内容
     */
	public T load(Serializable id) {
		return (T) getSession().load(clz, id);
	}

    /**
     * 获取单个对象,根据ID主键,但是和load方法的差别就是,这是立即获取对象的操作
     * @param id:主键id
     * @return
     */
	public T get(Serializable id) {
        return (T) getSession().get(clz, id);
    }

    /**
     * 更新数据条目(这个会更新所有的字段内容)
     * 所以一点要先获取到之前对应的数据库内容,否则会把之前的置空,特别注意
     * @param t:model类型
     */
	public void update(T t) {
		getSession().update(t);
	}

    /**
     * 删除数据条目(根据id)
     * @param id:主键id
     */
	public void deleteById(Serializable id) {
		getSession().delete(load(id));
	}

    /**
     * 进行有条件的更新操作的时候
     * @param hql:hql语句
     * @param paras:设置参数
     * @return 返回受到更新操作的数据条目的数目
     */
	public int executeUpdate(String hql, Object[] paras) {
		Query<Object> query = getSession().createQuery(hql);
		if (paras != null) {
			for (int i = 0; i < paras.length; i++) {
				query.setParameter(i, paras[i]);
			}
		}
		return query.executeUpdate();
	}

    /**
     * 进行有条件的更新操作(和executeUpdate方法作用一样,只是形参不一样)
     * @param sql:sql语句
     * @param paras:参数
     * @return 返回受到影响个数据条目数目
     */
	public int executeSQLUpdate(String sql, Object[] paras) {
		NativeQuery sqlQuery = getSession().createNativeQuery(sql);
		if (paras != null) {
			for (int i = 0; i < paras.length; i++) {
				sqlQuery.setParameter(i, paras[i]);
			}
		}
		return sqlQuery.executeUpdate();
	}

    /**
     * 获取session工厂的实例化对象
     * @return
     */
	private SessionFactoryImplementor getSessionFactoryImplementor() {
		SessionFactoryImpl sfi = (SessionFactoryImpl) getSession().getSessionFactory();
		return sfi;
	}
	
	/**
	 * 进行删除操作(不可以轻易使用)
     * 不要乱用这个方法,尤其是在表之间有关联的情况下
	 */
	public void deleteAll() {
		getSession().createNativeQuery("delete from `" + clz.getSimpleName().toLowerCase() + "`").executeUpdate();
	}

    /**
     * 这个方法没什么作用
     * @param hql
     * @param paras
     */
	protected void appendManagerSql(StringBuilder hql, List<Object> paras) {}

    /**
     * 返回一个对象类型数据
     * @param sql:sql语句
     * @param paras:设置参数
     * @return 返回一个对应的model类型
     */
	protected Object executeSQLQueryUniqueResult(String sql, List<? extends Object> paras) {
	    SQLQuery sqlQuery = getSession().createNativeQuery(sql);
	    if (CollectionUtils.isNotEmpty(paras)) {
	        for (int i = 0; i < paras.size(); i++) {
	            sqlQuery.setParameter(i, paras.get(i));
	        }
	    }
	    return sqlQuery.uniqueResult();
	}

    /**
     * 返回多个对象数据类型
     * @param sql
     * @param paras
     * @return
     */
	protected List<Object[]> executeSQLQuery(String sql, List<? extends Object> paras) {
        SQLQuery sqlQuery = getSession().createNativeQuery(sql);
        if (CollectionUtils.isNotEmpty(paras)) {
            for (int i = 0; i < paras.size(); i++) {
                sqlQuery.setParameter(i, paras.get(i));
            }
        }
        return sqlQuery.list();
    }

    /**
     * 具有分页处理,返回多个数据对象
     * @param sql:sql语句
     * @param paras:设置参数
     * @param page:分页对象page
     * @return 返回通过分页处理的多条数据
     */
	protected List<Object[]> executeSQLQuery(String sql, Object[] paras,Page page) {
        SQLQuery sqlQuery = getSession().createNativeQuery(sql);
        if (paras != null && paras.length > 0) {
            for (int i = 0; i < paras.length; i++) {
                sqlQuery.setParameter(i, paras[i]);
            }
        }
        if (page != null) {
			if (StringUtils.isNotBlank(page.getSort())) {
			}

			String countSql = "select count(*) from (" + sql + ") as aaaaaaaaaaaaaaa"; 
			int totalCount = countBySql(countSql, paras);
			page.setTotalRecords(totalCount);
			int totalPages = totalCount % page.getPageSize() == 0 ? (totalCount / page.getPageSize()) :(totalCount / page.getPageSize() + 1); 
			page.setTotalPages(totalPages);
			sqlQuery.setFirstResult((page.getPage() - 1) * page.getPageSize());
			sqlQuery.setMaxResults(page.getPageSize());
		}
        return sqlQuery.list();
    }

    /**
     * 比较原始的获取多条数据的方法
     * @param sql:sql语句
     * @param paras:设置参数
     * @param class1:设置返回的对象的model类型
     * @return 返回多条数据
     */
	@SuppressWarnings("rawtypes")
    public List findBySQL(String sql, Object[] paras, Class<? extends Object> class1) {
        if (logger.isDebugEnabled()) {
            logger.debug("sql: " + sql);
            logger.debug("paras: " + Arrays.toString(paras));
        }
            Connection connection = null;
        try {
             connection = dataSource.getConnection();
             PreparedStatement stmt = connection.prepareStatement(sql);
                if (paras != null) {
                    for (int i = 0; i < paras.length; i++) {
                        stmt.setObject(i + 1, paras[i]);
                    }
                }
                ResultSet rs = stmt.executeQuery();
                List list = new ArrayList();
                ResultSetMetaData md = rs.getMetaData();
                int columnCount = md.getColumnCount();
                List<String> dbColumnNames = new ArrayList<String>();
                for (int i = 1; i <= columnCount; i++) {
                    dbColumnNames.add(md.getColumnName(i));
                }
                while (rs.next()) {
                    List<Object> dbColumns = new ArrayList<Object>();
                    for (int i = 1; i <= columnCount; i++) {
                        dbColumns.add(rs.getObject(i));
                    }
                    Object t = toModel(dbColumnNames, dbColumns, class1);
                    list.add(t);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("sql: " + sql);
                    logger.debug("paras: " + Arrays.toString(paras));
                    logger.debug("results: " + list);
                }
                return list;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                }
            }
        }
    }

    /**
     * 将对象转为model类
     * @param dbColumnNames
     * @param dbColumns
     * @param class1
     * @return
     */
    private Object toModel(List<String> dbColumnNames, List<Object> dbColumns, Class<? extends Object> class1) {
        try {
            Object t = class1.newInstance();
            for (int i = 0; i < dbColumnNames.size(); i++) {
                String name = dbColumnNames.get(i);
                Method m = getSetter(name, class1);
                Class<? extends Object> type = m.getParameterTypes()[0];
                Object value = dbColumns.get(i);
                if (value == null) {
                	return null;
                }
                if (type == Long.class || type == long.class) {
                    value = Long.valueOf(value.toString());
                } else if (type == Integer.class || type == int.class) {
                    value = Integer.valueOf(value.toString());
                } else if (type == String.class) {
                    value = value.toString();
                } else if (type == BigDecimal.class) {
                    value = new BigDecimal(value.toString());
                } else if (type == Date.class) {
                    value = (Date) value;
                } else if (type == Timestamp.class) {
                    value = (Timestamp) value;
                } else if (type == Double.class || type == double.class) {
                    value = Double.valueOf(value.toString());
                } else if (type == Boolean.class || type == boolean.class) {
                    value = Boolean.valueOf(value.toString());
                }
                m.invoke(t, value);
            }
            return t;
        } catch (IllegalArgumentException e) {
            logger.error(e, e);
            e.printStackTrace();
        } catch (InstantiationException e) {
            logger.error(e, e);
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            logger.error(e, e);
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            logger.error(e, e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取数据列XXX的model中的setXXX的方法
     * @param name
     * @param clazz
     * @return
     */
    private Method getSetter(String name, Class<? extends Object> clazz) {
        //反射获取对应clazz中model的所有的方法名
        Method[] ms = clazz.getDeclaredMethods();
        for (Method m : ms) {
            if (("set" + name).equalsIgnoreCase(m.getName())) {
                return m;
            }
        }
        return null;
    }

    /**
     * 获取查询语句对应的获取数据的条目数
     * @param hql:hql语句
     * @param params:设置参数
     * @return 返回查询到的数据的个数
     */
    public int countByHql(String hql, Object[] params) {
        Query query = getSession().createQuery(hql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i, params[i]);
            }
        }
        //query.uniqueResult()注意返回的是一个Long型,如果要转为Integer型,那么可以采取先转为string,然后再转为Integer,或者直接先转为Long,再转Integer
        return Integer.parseInt(query.uniqueResult().toString());
    }

    /**
     * 返回查询语句的查询结果的个数
     * @param sql:sql语句
     * @param params:设置参数
     * @return 返回查询的结果总个数
     */
    protected int countBySql(String sql, Object[] params) {
        SQLQuery query = getSession().createNativeQuery(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i, params[i]);
            }
        }
                
        return Integer.parseInt(query.uniqueResult().toString());
    }

    /**
     * 获取到多个对应model的对象
     * @param sql:sql语句
     * @param paras:设置参数
     * @param page:页面对象
     * @return 返回多个model对象
     */
    public List<T> findModelBySql(String sql, Object[] paras, Page page) {
    	NativeQuery sqlQuery = getSession().createNativeQuery(sql);
    	setQueryParameters(paras, sqlQuery);
    	sqlQuery.addEntity(clz);
    	
    	 if (page != null) {
             String countSql = "select count(*) from (" + sql + ") ";
             int totalCount = countBySql(countSql, paras);
             page.setTotalRecords(totalCount);
             int totalPages = totalCount % page.getPageSize() == 0 ? (totalCount / page.getPageSize()) : (totalCount / page.getPageSize() + 1);
             page.setTotalPages(totalPages);
             sqlQuery.setFirstResult((page.getPage() - 1) * page.getPageSize());
             sqlQuery.setMaxResults(page.getPageSize());
         }
    	
    	return sqlQuery.list();
    }
    
    /**
     * 传入sql,返回一个model
     * @param sql sql语句, 比如select * from e_sys_user
     * @param paras:设置参数
     * @return 返回一个model对象
     */
    public <K> K findOneModelBySql(String sql, Object[] paras, Class<? extends BaseModel> clzz) {
    	NativeQuery<K> sqlQuery = getSession().createNativeQuery(sql);
    	setQueryParameters(paras, sqlQuery);
    	sqlQuery.addEntity(clzz);
        sqlQuery.setFirstResult(0);
        sqlQuery.setMaxResults(1);
    	List<K> list = sqlQuery.list();
    	return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    /**
     * 设置sql语句中的参数
     * @param paras:参数
     * @param sqlQuery:sql查询对象NativeQuery
     */
	private void setQueryParameters(Object[] paras, NativeQuery sqlQuery) {
		if (paras == null) {
			return;
		}
		for (int i = 0; i < paras.length; i++) {
			sqlQuery.setParameter(i, paras[i]);
		}
	}

	/**
	 * 通过userid找到对应的员工信息
	 * @param userid
	 * @return
	 */
	public Employee findByUserId(String hql , Long userid) {
		Query query = getSession().createQuery(hql);
		query.setParameter(0, userid);
		return (Employee) query.uniqueResult();
	}
}
(2)使用的测试实例:
package com.hnu.scw.salary.dao;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import com.hnu.scw.salary.dto.DepartmentDTO;
import com.hnu.scw.salary.model.Department;
import com.hnu.scw.salary.support.Page;

@Repository
public class DepartmentDAO extends BaseDAO<Department> {

    public List<Department> find(DepartmentDTO dto, Page page) {
        StringBuilder hql = new StringBuilder();
        List<Object> paras = new ArrayList<Object>();
        hql.append("From Department n where 1 = 1 ");
        if (dto.getId() != null) {
            hql.append(" and n.id = ? ");
            paras.add(dto.getId());
        }
        if (StringUtils.isNotBlank(dto.getCode())) {
            hql.append(" and n.code = ? ");
            paras.add(dto.getCode());
        }		
        if (StringUtils.isNotBlank(dto.getName())) {
            hql.append(" and n.name like ? ");
            paras.add("%" + dto.getName() + "%");
        }					
        if (dto.getManager() != null && dto.getManager().getId() != -1) {
        	if (dto.getManager().getId() != null) {
	            hql.append(" and n.manager.id = ? ");
	            paras.add(dto.getManager().getId());
        	}
        }			
        hql.append(" order by n.id desc ");
        return super.find(hql.toString(), paras.toArray(), page);
    }
    
    public Department findOne(DepartmentDTO dto) {
		Page page = new Page();
		page.setPageSize(1);
		page.setPage(1);
		
		List<Department> list = this.find(dto, page);
	    return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }
    
    @Override
	public Long create(Department department) {
		return super.create(department);
	}

	@Override
	public void update(Department department) {
		super.update(department);
	}
    
}
感谢各位的阅读,会不断的进行更新的哦~!~!欢迎关注~!。。。。。。。。。。。。

概述bearcat-dao 是一个 node.js 基于 SQL mapping 的 DAO 框架。实现了基于 SQL mapping 来对数据结果集进行映射,是一种半自动化的模式,相比较于 O/R mapping 全自动化的模式。 因此,在 bearcat-dao 里,开发者能够对SQL进行完全的控制,通过SQL来与数据库打交道并进行性能优化,bearcat-dao 则会把数据结果集映射到 bearcat model 中去。SQL mapping vs O/R mapping结构化查询语言(SQL)已经存在了非常久的时间。自从 Edgar F.Codd 第一次提出“数据可以被规范化为一组相互关联的表”这样的思想以来,已经超过35年了。很少有哪一种软件技术敢声称自己像关系数据库和SQL那样经受住了时间的考验。因此,关系数据库和SQL仍然很有价值,我们可能都曾有这样的经历,应用程序的源代码(经历了很多版本)随着时间的流逝最终还是过时了(无法维护下去),但它的数据库甚至是SQL本身却仍然很有价值。O/R mapping 被设计为用来简化对象持久化工作的,它通过将SQL完全从开发人员的职责中排除来达到这个目的。在O/R mapping中,SQL是给予应用程序中的类与关系数据库表之间的映射关系而生成的。除了不用写SQL语句,使用O/R mapping的API通常也比典型的SQL API要简单很多,但是O/R mapping仍然不是一颗“银弹”,它并非适用于所有的场景。一个最主要的问题就是O/R mapping它需要假设数据库是被恰当的规范化了,如果没有被恰当规范,这就会给映射带来许多麻烦,甚至需要绕些弯路,或者在设计时对效率做些折衷。同时,没有哪一个对象/关系解决方案可以支持每一种数据库的每一种特性、每一种能力以及设计上固有的缺陷,它们仅仅能做到一个子集,而能做到全集的恰恰则是SQL这个专为数据库设计的结构化查询语言SQL mapping 与 O/R mapping 不同,它不是直接把类映射为数据库表或者说把类的字段映射为数据库列,而是把SQL语句与结果(也即输入和输出)映射为类。bearcat-dao 在类(model)和数据库之间建立了一个额外的中间层,这就为如何在类和数据库表之间建立映射关系带来了更大的灵活性,使得在不用改变数据模型或者对象模型的情况下改变它们的映射关系成为可能。这个中间层其实就是SQL,通过SQL可以将类(model)与数据库表之间的关系降到最低。开发者只需要编写SQL,bearcat-dao 负责在类(model)属性与数据库表的列之间映射参数和结果Modelmodel 定义使用 bearcat model因此,可以非常容易的就设置映射关系、约束、relation关系例如,我们有一个 test 表,它只有一个 id 主键字段create table test(     id bigint(20) NOT NULL COMMENT 'id',              PRIMARY KEY (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; 然后,我们可以定义下面的 modelvar TestModel = function() {     this.$mid = "testModel";     this.$table = "test";     this.id = "$primary;type:Number"; }    module.exports = TestModel;在 TestModel 里,我们使用 $table 属性来设置需要映射的表名,对于 id 属性,我们用 primary 表明这是一个主键,并且我们给这个字段添加了一个 type 约束,限定它一定为 Number 类型Relation 在关系型数据库的表与表之间是可以有 relation 的,也即关系,有一对一、一对多、多对多这三种情况一对一 relation一对一关系意味着两张表,一张表有另外一张表的id引用(或者外键)。在model对象里面就是说,两个model,是一对一的比如,我们有两张表,test1 表有对 test2 表的 id 引用create table test1(     id bigint(20) NOT NULL COMMENT 'id',         rid bigint(20) NOT NULL COMMENT 'reference to test2 id',                PRIMARY KEY (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8;create table test2(     id bigint(20) NOT NULL COMMENT 'id',              PRIMARY KEY (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8;然后,我们就可以定义这样的 modelvar Test1Model = function() {     this.$mid = "test1Model";     this.$table = "test1";     this.id = "$primary;type:Number";     this.test2 = "$type:Object;ref:test2Model" }    module.exports = Test1Model;var Test2Model = function() {     this.$mid = "test2Model";     this.$table = "test2";     this.id = "$primary;type:Number"; }    module.exports = Test2Model;通过用 Test1Model.test2 属性,我们使用 ref:test2Model 来设置对 test2Model 的引用一对多 relation一对多则意味着,一个model引用着另外一个model数组。比如,我们有一个博客,这个博客里面的文章有很多评论,这个博客文章与评论之间的关系就是一对多的var Test1Model = function() {     this.$mid = "test1Model";     this.$table = "test1";     this.id = "$primary;type:Number";     this.test2 = "$type:Array;ref:test2Model" }    module.exports = Test1Model;在上面的model定义中,我们简单的把 test2 属性的 type 改成 Array 即可,它就变成了一对多的关系多对多 relation多对多一般可以通过中间表,来转化成两个一对多的关系SQL 模板当编写复杂sql语句的时候,如果仅仅使用 String 类型的字符串来编写,肯定非常痛苦,更好的方式是用 SQL 模板编写SQL模板相当简单比如,我们可以定义 id 为 testResultSql 的 SQL 模板sql testResultSql select * from test  end然后我们可以在dao中使用这个 SQL 模板domainDaoSupport.getList("$testResultSql", null, "testModel", function(err, results) {      // results is testModel type array });第一个参数,开头带上 $ 就表面是一个 SQL 模板同时,由于是模板,因此可以包含其他模板,比如sql testResultSql select * from ${testResultTable}  end sql testResultTable test end这个结果和上面是一样的ResultSet 映射数据库结果集是一个由field/value对象组成的数组,因此映射结果集就像用特定key/value对来填充对象。为了能够做到匹配,我们使用 model 属性值里的 prefix model magic attribute value 或者 model 属性里的 prefixmodel attribute比如,如果你查询得到了如下的 resultSet[{     "id": 1,     "title": "blog_title",     "content": "blog_content",     "create_at": 1234567,     "update_at": 1234567 }]那么,映射的model就是这样的var BlogModel = function() {     this.$mid = "blogModel";     this.$table = "ba_blog";     this.id = "$primary;type:Number";     this.aid = "$type:Number";     this.title = "$type:String";     this.content = "$type:String";     this.create_at = "$type:Number";     this.update_at = "$type:Number"; }    module.exports = BlogModel;如果结果集字段是已 ***blog_***开头,比如[{     "blog_id": 1,     "blog_title": "blog_title",     "blog_content": "blog_content",     "blog_create_at": 1234567,     "blog_update_at": 1234567 }]那么,映射model就是这样的var BlogModel = function() {     this.$mid = "blogModel";     this.$table = "ba_blog";     this.$prefix = "blog_";     this.id = "$primary;type:Number";     this.aid = "$type:Number";     this.title = "$type:String";     this.content = "$type:String";     this.create_at = "$type:Number";     this.update_at = "$type:Number"; }    module.exports = BlogModel;仅仅需要添加 this.$prefix model 属性DAODAO 是领域对象模型的缩写,一般用于操作数据库bearcat-dao 提供 domainDaoSupport 对象,封装了基本的sql、cache操作。使用它也非常简单,直接依赖注入,然后通过 init 方法进行初始化simpleDao.jsvar SimpleDao = function() {     this.$id = "simpleDao";     this.$init = "init";     this.$domainDaoSupport = null; }    SimpleDao.prototype.init = function() {     // init with SimpleModel id to set up model mapping     this.domainDaoSupport.initConfig("simpleModel"); }    // query list all // callback return mapped SimpleModel array results SimpleDao.prototype.getList = function(cb) {     var sql = ' 1 = 1';     this.$domainDaoSupport.getListByWhere(sql, null, null, cb); }    module.exports = SimpleDao;完整的api可以参见 domainDaoSupport配置使用修改项目中的context.json placeholds 可以很方便的在不同环境间切换"dependencies": {     "bearcat-dao": "*" }, "beans": [{     "id": "mysqlConnectionManager",     "func": "node_modules.bearcat-dao.lib.connection.sql.mysqlConnectionManager",     "props": [{         "name": "port",         "value": "${mysql.port}"     }, {         "name": "host",         "value": "${mysql.host}"     }, {         "name": "user",         "value": "${mysql.user}"     }, {         "name": "password",         "value": "${mysql.password}"     }, {         "name": "database",         "value": "${mysql.database}"     }] }, {     "id": "redisConnectionManager",     "func": "node_modules.bearcat-dao.lib.connection.cache.redisConnectionManager",     "props": [{         "name": "port",         "value": "${redis.port}"     }, {         "name": "host",         "value": "${redis.host}"     }] }]如果你不需要使用redis, 你可以移除redisConnectionManager定义事务bearcat-dao 基于 bearcat AOP 提供了事务支持. aspect 是 transactionAspect , 提供了 around advice, 当目标事务方法调用cb函数的时候传入了 err, rollback 回滚操作就会被触发, 相反如果没有cb(err)的话, 事务就会被提交(commit).pointcut 定义的是:"pointcut": "around:.*?Transaction"因此, 任何已 Transaction 结尾的POJO中的方法都会匹配到 transaction 事务由于transaction必须在同一个connection中, 在 bearcat-dao 中是通过 transactionStatus 来保证的, 在同一个事务的 transaction 必须在同一个transactionStatus中SimpleService.prototype.testMethodTransaction = function(cb, txStatus) {     var self = this;     this.simpleDao.transaction(txStatus).addPerson(['aaa'], function(err, results) {         if (err) {             return cb(err); // if err occur, rollback will be emited         }         self.simpleDao.transaction(txStatus).getList([1, 2], function(err, results) {             if (err) {                  return cb(err); // if err occur, rollback will be emited             }             cb(null, results); // commit the operations         });     }); }开启 Debug 模式跑node应用时带上BEARCAT_DEBUG为trueBEARCAT_DEBUG=true node xxx.js开启debug模式后,就能看到具体执行SQL的日志例子bearcat-todobearcat-dao example 标签:bearcat
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值