您的位置:首页 > 其它

[框架那点事儿-快速开发季]编写自己的数据持久层(4)分页查询

2010-07-29 13:37 633 查看
上一章节讲述的查询,都是不带分页的查询,但是在实际应用中,有相当一部分的需求是针对于分页查询的,比如页面的分页功能等。所以,作为底层的API,势必不能缺失针对于分页的查询接口封装。这一章节,我们提供以下几个分页查询,分别针对于:对象查询、部分字段查询两大类。

/**
* 分页查询对象
* <p>
* 	根据指定参数分页查询对象列表
* 	用法示例:
*  ----------------------------------------------------------
*  //设置查询参数
*  Map<String,Object> params = new HashMap();
*  params.put("city","hangzhou");
*
*  Paginal p = queryObjectListForPaging(Person.class,params,1,10);
*  ...
* </p>
* @param objectClass 对象类型
* @param params	查询参数
* @param pageNomber	页号
* @param pageSize		分页大小
* @return 分页封装
*/
@SuppressWarnings({ "rawtypes"})
public Paginal queryObjectListForPaging(final Class objectClass,Map<String,Object> params,int pageNomber,int pageSize){
return queryObjectListForPagingOrderRow(objectClass,params,pageNomber,pageSize);
}

/**
* 分页-排序查询对象
* <p>
* 	分页查询,结果按照指定的条件排序
*  使用示例
*  ----------------------------------------------------------
*  //设置查询参数
*  Map<String,Object> params = new HashMap();
*  params.put("city","hangzhou");
*
*  Map<String,String> orderByAge = new HashMap<String,String>();
*  orderByName.put("age","desc");
*
*  Map<String,String> orderByType = new HashMap<String,String>();
*  orderByName.put("type","asc");
*
*  Paginal p = queryObjectListForPagingOrderRow(Person.class,params,1,10,orderByAge,orderByType);
*  ...
* </p>
* @param objectClass 字段所在类
* @param params 查询参数
* @param pageNomber 页号
* @param pageSize 分页大小
* @param orders 排序字段
* @return 查询结果
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Paginal queryObjectListForPagingOrderRow(final Class objectClass,Map<String,Object> params,int pageNomber,int pageSize,Map<String,String>... orders){
//~~~ return value
Paginal<? extends Object> paginal = new Paginal<Object>();

// 初始化分页器
paginal.setPageNomber(pageNomber);
paginal.setPageSize(pageSize);

//获取表名
tableName = getTableConfigName(objectClass);
//组装查询sql语句
String sql = buildQuerySql(null, params, paginal.getOffset(),paginal.getPageSize(),false,orders);
//查询总记录数
int totalCount = queryCount(params,objectClass);

paginal.setTotalCount(totalCount);//总记录数
paginal.setPageNum(totalCount,pageSize);//总页码

//执行查询
List resultList = queryForList(sql,params,objectClass);
paginal.setResultList(resultList);

return paginal;
}

/**
* 分页查询部分字段
* <p>
* 	使用示例
*  -----------------------------------------------------------------
*  //设置查询参数
*  Map<String,Object> params = new HashMap();
*  params.put("city","hangzhou");
*
*  //设置需要查询的字段名称,名称使用DO中的即可
*  List<String> fields = new ArrayList<String>();
*  fields.add("id");
*  fields.add(name);
*  fields.add("address");
*
*  Paginal p = queryFieldsListForPaging(objectClass,params,fields,1,10);
*  ...
* </p>
* @param objectClass 字段所在的类
* @param params 查询参数
* @param returnFields 需要查询的字段列表
* @param pageNomber 页号
* @param pageSize 分页大小
* @return 查询结果
*/
@SuppressWarnings({ "rawtypes"})
public Paginal queryFieldsListForPaging(final Class objectClass,Map<String,Object> params,List<String> returnFields,int pageNomber,int pageSize){
return queryFieldsListForPagingOrderRow(objectClass,params,returnFields,pageNomber,pageSize);
}

/**
* 分页-排序查询部分字段
* <p>
* 	使用示例
*  -----------------------------------------------------------------
*  //设置查询参数
*  Map<String,Object> params = new HashMap();
*  params.put("city","hangzhou");
*
*  //设置需要查询的字段名称,名称使用DO中的即可
*  List<String> fields = new ArrayList<String>();
*  fields.add("id");
*  fields.add(name);
*  fields.add("address");
*
*  Map<String,String> orderByAge = new HashMap<String,String>();
*  orderByName.put("age","desc");
*
*  Map<String,String> orderByType = new HashMap<String,String>();
*  orderByName.put("type","asc");
*
*  Paginal p = queryFieldsListForPaging(objectClass,params,fields,1,10,orderByAge,orderByType);
*  ...
* </p>
* @param objectClass 字段所在的类
* @param params 查询参数
* @param returnFields 需要查询的字段列表
* @param pageNomber 页号
* @param pageSize 分页大小
* @param orders 排序字段
* @return 查询结果
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Paginal queryFieldsListForPagingOrderRow(final Class objectClass,Map<String,Object> params,List<String> returnFields,int pageNomber,int pageSize,Map<String,String>... orders){
//~~~ return value
Paginal<? extends Object> paginal = new Paginal<Object>();

// 初始化分页器
paginal.setPageNomber(pageNomber);
paginal.setPageSize(pageSize);

//获取表名
tableName = getTableConfigName(objectClass);
//组装查询sql语句
String sql = buildQuerySql(returnFields, params, paginal.getOffset(),paginal.getPageSize(),false,orders);
//查询总记录数
int totalCount = queryCount(params,objectClass);

paginal.setTotalCount(totalCount);//总记录数
paginal.setPageNum(totalCount,pageSize);//总页码

//查询 得到 List<Map<String,Object>>
List resultList = queryForMapList(sql,params,objectClass);
paginal.setResultList(resultList);

return paginal;
}

/**
* 查询总记录数
* <p>
* 	根据指定查询条件查询记录总数,一般用于分页中等
* </p>
* @param params 查询条件参数
* @param objectClass 对象类型
* @return 记录数
*/
@SuppressWarnings({ "rawtypes"})
public int queryCount(Map<String,Object> params,Class objectClass){
//获取表名
tableName = getTableConfigName(objectClass);

//组装查询总记录数sql语句
String countsql = buildQueryConteSql(params);
//查询对象,存储查询条件数组
Object[] queryObject = getQueryObject(params);
//查询总记录数
int totalCount = getJdbcTemplate().queryForInt(countsql, queryObject);

return totalCount;
}


以上提供了几个分页查询api,封装的思路大致是:

分页查询 -> 带排序的分页查询

其实各种查询归根结底就是查询使用的sql的不同,由于之前我们封装的sql的生成方法和采用可编程度参数指定查询排序字段的方式,使得后续的工作变得很顺利。

分页器:

package com.netease.space.dao.base;
import java.io.Serializable;
import java.util.List;
/**
* 分页器
* <p>
* 	mysql的分页机制:limit offset,pagesize [] 从0开始计数偏移offset位的pagesize条数据
*  比如:
*  pagesize = 10;
*  -------------------------------------------------
*  | 页号  | 起始地址  | 结束地址  | 包含记录ID   |offset|
*  -------------------------------------------------
*  | 1    |    0    |    9    |0,1,2...9     |  0   |
*  | 2    |    10   |    19   |10,11,12...19 |  10  |
*  | 3    |    20   |    29   |20,22,23...29 |  20  |
*  --------------------------------------------------
*
*  用法示例:
*   limit getOffset(),pagesize;
* </p>
* @author quzishen
* @param <T> 查询结果的具体类型
*/
public class Paginal<T extends Object> implements Serializable{
private static final long serialVersionUID = -5159256681785242925L;
// 分页大小
private int pageSize;
// 页号,从1开始
private int pageNomber;
// 总记录数
private int totalCount;
// 总页数
private int pageNum;
// 查询对象列表
private List<T> resultList;

/**
* 获取当前页的起始地址
* @return 起始地址
*/
protected int getBeginIndex(){
return (pageNomber - 1)*pageSize;
}

/**
* 获取当前页的结束地址
* @return 结束地址
*/
protected int getEndIndex(){
return pageNomber*pageSize - 1;
}

/**
* 获取当前页的偏移量
* @return 偏移量
*/
protected int getOffset(){
if(pageNomber == 1){
return 0;
} else {
return (pageNomber-1)*pageSize;
}
}

/**
* 根据总记录数和分页大小设置总页数
* @param totalCount 总记录数
* @param pagesize 分页大小
*/
public void setPageNum(int totalCount,int pagesize){
int num = totalCount / pagesize;
if(0 != totalCount % pagesize){
num += 1;
}

pageNum = num;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageNomber() {
return pageNomber;
}
public void setPageNomber(int pageNomber) {
this.pageNomber = pageNomber;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public List<T> getResultList() {
return resultList;
}
public void setResultList(List<T> resultList) {
this.resultList = resultList;
}
}


请看详细代码:

package com.netease.space.dao.base;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* DAO抽象父类,提供针对于数据库的基本操作
* 工程名称:NormandyPosition
* 类型名称:AbstractBaseDAO
* 概要:
* <p>
* 提供针对于DAO基础操作服务
* 注意使用该类提供的API,必须保证DO类的属性名称,与对应数据库表的名称相同,不区分大小写
* </p>
* 创建时间:2010-7-27 上午12:58:42
* 创建人:quzishen
* 最后修改时间:2010-7-27 上午12:58:42
* 最后修改内容:
* @version 1.0
*/
public abstract class AbstractBaseDAO extends JdbcDaoSupport implements InitializingBean{
//~~~ instance fields

// table name
private String tableName;
//used for insert
SimpleJdbcInsert inserActor;
//default getter method prefix
public String GET_METHOD_PRE = "get";
//default setter method prefix
public String SET_METHOD_PRE = "set";
//default key name
public String DEFAULT_ID_NAME = "id";
//default serialVersionUID name
public String DEFAULT_SERIALVERSIONUID_NAME = "serialVersionUID";

//~~~ static final fields start ****************//
private final static String SQL_SELECT = " select ";
private final static String SQL_FROM = " from ";
private final static String SQL_WHERE = " where ";
private final static String SQL_AND = " and ";
private final static String SQL_UPDATE = " update ";
private final static String SQL_SET = " set ";
private final static String SQL_ORDER_BY = " order by ";
private final static String SQL_LIMIT = " limit ";
private final static String SQL_COUNT = " count(*) ";

private final static String SQL_EQUAL = " = ";
private final static String SQL_SPLIT = " , ";
private final static String SQL_PLACE_HOLDER = " ? ";
//~~~ static final fields end ****************//

/**************************************///public method begin /*******************************/
/**
* 新增一条记录
* <p>
* 要求DO:遵循spring风格的getter和setter方法
* 属性的名称和数据库的名称要对应,大小写不区分
* 示例:
* -----------------------------------------------------
* TABLE Person{
* ID BIGINT NOT NULL,
* NAME VARCHAR(16) ....
* }//数据库中字段为大写
* -----------------------------------------------------
* class Person{
* private long id;
* private String name;
* }//DO类中的字段为小写
* -----------------------------------------------------
* Person person = new Person();
* person.setName("quzishen");
* long id = insertObject(person);
*
* </p>
* @param 插入对象
* @return 主键ID
* @throws RuntimeException
*/
public int insertObject(Object object) throws RuntimeException{
// 获取类型
@SuppressWarnings("rawtypes")
Class objectClass = object.getClass();
//获取表名
tableName = getTableConfigName(objectClass);
// 使用SimpleJdbcInsert完成插入的动作
inserActor = new SimpleJdbcInsert(getJdbcTemplate()).withTableName(tableName);

// 获取该对象的所有的private protected publick的属性
Field[] fields = getFieldsFromClass(objectClass);
// 将属性名字保存下来
List<String> fieldNameSet = getFiledNameList(fields);
// 设置需要更新的字段以及主键
setUsingListFromNameSet(fieldNameSet);
// 获取插入数据库sql需要的参数Map
Map<String, Object> paramMap = getParamMap(object, fields);
//简便起见,可以使用SqlParameterSource parameters = new BeanPropertySqlParameterSource(Actor);
// 获取到的参数为空
if(null == paramMap){
throw new RuntimeException("can not insert a null value!");
}

//执行插入并返回主键
return inserActor.executeAndReturnKey(paramMap).intValue();
}

/**
* 查询单一对象
* <p>
* 要求:遵循spring风格的getter和setter方法
* 属性的名称和数据库的名称要对应,大小写不区分
* 这里一定要注意,如果属性的大小写有点个性非常强的个人色彩,
* 那么相应的setter方法要保证'set'后的属性首字母大写,其他字母大小写保持不变
* 如:pArAm ----> getPArAm & setPArAm
* 示例:
* -------------------------------------------------------------
* class Person{
* private long id;
* }
* -------------------------------------------------------------
* Map<String,Object> params = new HashMap<String,Object>();
* params.put("id","10000");
* Person person = (Person) queryForObject(params,Person.class);
*
* </p>
* @param params 查询条件map,key 属性名字 大小写无所谓
* @param returnClass 对象类型
* @return 查询结果
*/
public Object queryForObject(Map<String,Object> params,@SuppressWarnings("rawtypes") final Class returnClass) throws RuntimeException{
// 组装查询sql,参数使用占位符
String sql = buildQuerySql(null,params,-1,-1,false);

//获取表名
tableName = getTableConfigName(returnClass);

// 查询对象,存储查询条件数组
Object[] queryObject = getQueryObject(params);

//调用queryForObject并且重新RowMapper回调方法
return getJdbcTemplate().queryForObject(sql, queryObject,new RowMapper() {
public Object mapRow(ResultSet resultSet, int rowNum) throws SQLException {
if(1 < rowNum){
throw new RuntimeException("query result is more than one!");
}
//~~~ return value 组装返回对象
Object object = getObjectFromResultSet(resultSet,returnClass);

return object;
}
});
}

/**
* 查询对象列表结果
* <p>
* 要求:遵循spring风格的getter和setter方法
* 属性的名称和数据库的名称要对应,大小写不区分
* 这里一定要注意,如果属性的大小写有点个性非常强的个人色彩,
* 那么相应的setter方法要保证'set'后的属性首字母大写,其他字母大小写保持不变
* 如:pArAm ----> getPArAm & setPArAm
* 示例:
* -------------------------------------------------------------
* class Person{
* private String desc;
* }
* -------------------------------------------------------------
* Map<String,Object> params = new HashMap<String,Object>();
* params.put("desc","10000");
* List<Person> persons = (List<Person>) queryForList(params,Person.class);
*
* </p>
* @param params 参数Map
* @param returnClass 返回列表中的对象类型
* @return
*/
@SuppressWarnings("rawtypes")
public List queryForObjectList(Map<String,Object> params,final Class returnClass){
return queryForObjectListOrderRow(params,returnClass);
}
/**
* 查询对象列表结果,查询按照指定列排序
* <p>
* 关于排序列(可变长度参数部分):
* 根据排序的列的次序依次排列,每个Map中存放一个键值对,允许指定的排序为 asc || desc
*
* 示例:
* Map<String,String> orderByName = new HashMap<String,String>();
* orderByName.put("name","asc");
*
* Map<String,String> orderByAge = new HashMap<String,String>();
* orderByName.put("age","desc");
*
* Map<String,String> orderByType = new HashMap<String,String>();
* orderByName.put("type","asc");
*
* queryForListOrderRow(params,returnClass,orderByName,orderByAge,orderByType)
* </p>
* @param params
* @param returnClass
* @param orders
* @return
*/
@SuppressWarnings("rawtypes")
public List queryForObjectListOrderRow(Map<String,Object> params,
final Class returnClass,
Map<String,String>... orders){
// 组装查询sql,参数使用占位符替代
String sql = buildQuerySql(null,params,-1,-1,false,orders);
if(logger.isDebugEnabled()){
logger.debug("RUN SQL:"+sql);
}
//执行查询
return queryForList(sql,params,returnClass);
}

/**
* 查询部分字段
* <p>
* 根据指定的属性名称查询特定字段,返回一个List<Map<属性名称,属性值>>
*
* 使用示例:
* ---------------------------------------------------------------
* //设置查询参数
* Map<String,Object> params = new HashMap();
* params.put("city","hangzhou");
*
* //设置需要查询的字段名称,名称使用DO中的即可
* List<String> fields = new ArrayList<String>();
* fields.add("id");
* fields.add(name);
* fields.add("address");
*
* List<Map<String,Object>> resultList = queryFieldsList(Person.class,params,fields);
* ...
*
* </p>
* @param objectClass 返回属性所在的类
* @param objectClass
* @param params 查询参数map
* @param returnFields 返回字段列表
* @return 返回值,用map保存每一列的结果
*/
public List<Map<String,Object>> queryFieldsList(@SuppressWarnings("rawtypes")final Class objectClass,Map<String,Object> params,List<String> returnFields){
return queryFieldsListOrderRow(objectClass,params,returnFields);
}

/**
* 查询部分字段,查询结果根据指定列排序
* <p>
* 根据指定的属性名称查询特定字段,并且根据指定的列排序,返回一个List<Map<属性名称,属性值>>
* 使用示例:
* ---------------------------------------------------------------
* //设置查询参数
* Map<String,Object> params = new HashMap();
* params.put("city","hangzhou");
*
* //设置需要查询的字段名称,名称使用DO中的即可
* List<String> fields = new ArrayList<String>();
* fields.add("id");
* fields.add(name);
* fields.add("address");
*
* Map<String,String> orderByAge = new HashMap<String,String>();
* orderByName.put("age","desc");
*
* Map<String,String> orderByType = new HashMap<String,String>();
* orderByName.put("type","asc");
*
* List<Map<String,Object>> resultList = queryFieldsList(Person.class,params,fields,orderByAge,orderByType);
* ...
*
* </p>
* @param objectClass 返回属性所在的类
* @param params 查询参数map
* @param returnFields 返回字段列表
* @param orders 查询语句排序指定字段
* @return 返回值,用map保存每一列的结果
*/
@SuppressWarnings("unchecked")
public List<Map<String,Object>> queryFieldsListOrderRow(@SuppressWarnings("rawtypes")final Class objectClass,
Map<String,Object> params,List<String> returnFields,Map<String,String>... orders){
// 组装查询sql,参数使用占位符替代
String sql = buildQuerySql(returnFields,params,-1,-1,false,orders);

//获取表名
tableName = getTableConfigName(objectClass);

return queryForMapList(sql,params,objectClass);
}

/** * 分页查询对象 * <p> * 根据指定参数分页查询对象列表 * 用法示例: * ---------------------------------------------------------- * //设置查询参数 * Map<String,Object> params = new HashMap(); * params.put("city","hangzhou"); * * Paginal p = queryObjectListForPaging(Person.class,params,1,10); * ... * </p> * @param objectClass 对象类型 * @param params 查询参数 * @param pageNomber 页号 * @param pageSize 分页大小 * @return 分页封装 */ @SuppressWarnings({ "rawtypes"}) public Paginal queryObjectListForPaging(final Class objectClass,Map<String,Object> params,int pageNomber,int pageSize){ return queryObjectListForPagingOrderRow(objectClass,params,pageNomber,pageSize); } /** * 分页-排序查询对象 * <p> * 分页查询,结果按照指定的条件排序 * 使用示例 * ---------------------------------------------------------- * //设置查询参数 * Map<String,Object> params = new HashMap(); * params.put("city","hangzhou"); * * Map<String,String> orderByAge = new HashMap<String,String>(); * orderByName.put("age","desc"); * * Map<String,String> orderByType = new HashMap<String,String>(); * orderByName.put("type","asc"); * * Paginal p = queryObjectListForPagingOrderRow(Person.class,params,1,10,orderByAge,orderByType); * ... * </p> * @param objectClass 字段所在类 * @param params 查询参数 * @param pageNomber 页号 * @param pageSize 分页大小 * @param orders 排序字段 * @return 查询结果 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Paginal queryObjectListForPagingOrderRow(final Class objectClass,Map<String,Object> params,int pageNomber,int pageSize,Map<String,String>... orders){ //~~~ return value Paginal<? extends Object> paginal = new Paginal<Object>(); // 初始化分页器 paginal.setPageNomber(pageNomber); paginal.setPageSize(pageSize); //获取表名 tableName = getTableConfigName(objectClass); //组装查询sql语句 String sql = buildQuerySql(null, params, paginal.getOffset(),paginal.getPageSize(),false,orders); //查询总记录数 int totalCount = queryCount(params,objectClass); paginal.setTotalCount(totalCount);//总记录数 paginal.setPageNum(totalCount,pageSize);//总页码 //执行查询 List resultList = queryForList(sql,params,objectClass); paginal.setResultList(resultList); return paginal; } /** * 分页查询部分字段 * <p> * 使用示例 * ----------------------------------------------------------------- * //设置查询参数 * Map<String,Object> params = new HashMap(); * params.put("city","hangzhou"); * * //设置需要查询的字段名称,名称使用DO中的即可 * List<String> fields = new ArrayList<String>(); * fields.add("id"); * fields.add(name); * fields.add("address"); * * Paginal p = queryFieldsListForPaging(objectClass,params,fields,1,10); * ... * </p> * @param objectClass 字段所在的类 * @param params 查询参数 * @param returnFields 需要查询的字段列表 * @param pageNomber 页号 * @param pageSize 分页大小 * @return 查询结果 */ @SuppressWarnings({ "rawtypes"}) public Paginal queryFieldsListForPaging(final Class objectClass,Map<String,Object> params,List<String> returnFields,int pageNomber,int pageSize){ return queryFieldsListForPagingOrderRow(objectClass,params,returnFields,pageNomber,pageSize); } /** * 分页-排序查询部分字段 * <p> * 使用示例 * ----------------------------------------------------------------- * //设置查询参数 * Map<String,Object> params = new HashMap(); * params.put("city","hangzhou"); * * //设置需要查询的字段名称,名称使用DO中的即可 * List<String> fields = new ArrayList<String>(); * fields.add("id"); * fields.add(name); * fields.add("address"); * * Map<String,String> orderByAge = new HashMap<String,String>(); * orderByName.put("age","desc"); * * Map<String,String> orderByType = new HashMap<String,String>(); * orderByName.put("type","asc"); * * Paginal p = queryFieldsListForPaging(objectClass,params,fields,1,10,orderByAge,orderByType); * ... * </p> * @param objectClass 字段所在的类 * @param params 查询参数 * @param returnFields 需要查询的字段列表 * @param pageNomber 页号 * @param pageSize 分页大小 * @param orders 排序字段 * @return 查询结果 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Paginal queryFieldsListForPagingOrderRow(final Class objectClass,Map<String,Object> params,List<String> returnFields,int pageNomber,int pageSize,Map<String,String>... orders){ //~~~ return value Paginal<? extends Object> paginal = new Paginal<Object>(); // 初始化分页器 paginal.setPageNomber(pageNomber); paginal.setPageSize(pageSize); //获取表名 tableName = getTableConfigName(objectClass); //组装查询sql语句 String sql = buildQuerySql(returnFields, params, paginal.getOffset(),paginal.getPageSize(),false,orders); //查询总记录数 int totalCount = queryCount(params,objectClass); paginal.setTotalCount(totalCount);//总记录数 paginal.setPageNum(totalCount,pageSize);//总页码 //查询 得到 List<Map<String,Object>> List resultList = queryForMapList(sql,params,objectClass); paginal.setResultList(resultList); return paginal; } /** * 查询总记录数 * <p> * 根据指定查询条件查询记录总数,一般用于分页中等 * </p> * @param params 查询条件参数 * @param objectClass 对象类型 * @return 记录数 */ @SuppressWarnings({ "rawtypes"}) public int queryCount(Map<String,Object> params,Class objectClass){ //获取表名 tableName = getTableConfigName(objectClass); //组装查询总记录数sql语句 String countsql = buildQueryConteSql(params); //查询对象,存储查询条件数组 Object[] queryObject = getQueryObject(params); //查询总记录数 int totalCount = getJdbcTemplate().queryForInt(countsql, queryObject); return totalCount; }

/*****************************///private methods begin .../*****************************/

/**
* 查询列表
* @param sql 查询sql,其中参数部分使用占位符?代替具体的值
* @param params 查询条件值的map
* @param returnClass 返回值类型
* @return 查询列表
*/
@SuppressWarnings("rawtypes")
private List queryForList(String sql,Map<String,Object> params,final Class returnClass){
//获取表名
tableName = getTableConfigName(returnClass);

// 查询对象,存储查询条件数组
Object[] queryObject = getQueryObject(params);
//执行查询,设置rowMapper进行结果从List<Map> --> List<Object>的转换
return getJdbcTemplate().query(sql, queryObject,new RowMapper(){
public Object mapRow(ResultSet resultSet, int rowNum) throws SQLException {
//~~~ return value 组装返回对象
Object object = getObjectFromResultSet(resultSet,returnClass);

return object;
}
});
}

@SuppressWarnings({ "rawtypes","unchecked" })
private List queryForMapList(String sql,Map<String,Object> params,final Class objectClass){
//获取查询参数
Object[] queryObject = getQueryObject(params);
//获取对象声明的属性
final Field[] fields = getFieldsFromClass(objectClass);
return (List<Map<String,Object>>)getJdbcTemplate().query(sql, queryObject, new RowMapper(){
public Object mapRow(ResultSet resultSet, int count) throws SQLException {
try{
Map<String,? extends Object> resultMap = getResultMapFromResultSet(resultSet,fields);
return resultMap;
}catch(Exception e){
throw new RuntimeException("can not get result from resultSet!");
}
}
});
}

/**
* 获取注解中配置的表名称
* @param Class 注解所添加位置的类
* @return String 注解中配置的tablename
*/
protected String getTableConfigName(@SuppressWarnings("rawtypes") Class objectClass){
@SuppressWarnings("unchecked")
Table tableAnnotation = (Table)objectClass.getAnnotation(Table.class);
String tableName = tableAnnotation.tableName();
return tableName;
}

/**
* 根据class和resultSet填充对象
* 通过反射,调用setter方法设置DO
* @param resultSet 查询返回值
* @param returnClass 返回值类型
*/
private Object getObjectFromResultSet(ResultSet resultSet,@SuppressWarnings("rawtypes") Class returnClass) throws RuntimeException{
// ~~~ return value
Object object = getInstanceByClass(returnClass);

if(null != object){
if(null == resultSet){
return null;
}

// 根据返回类型获取fields
Field[] fields = getFieldsFromClass(returnClass);
try{
//获取结果map<属性名,属性值>,用于方便的获取属性对应的值
Map<String,? extends Object> resultMap = getResultMapFromResultSet(resultSet,fields);

//遍历列表设置值,通过反射调用setter方法设置
for(Field field : fields){
if(checkIsSerivalVersioinUID(field.getName())){
//如果是序列ID,掠过
continue;
}
// 获取方法名
String methodName = getSetMethod(field.getName());
// 获取方法
Method method = object.getClass().getMethod(methodName, field.getType());
// 获取参数
Object param = resultMap.get(field.getName());//不能直接从resultSet中取值,因为其中的name是数据库的列名
// 执行setter方法
method.invoke(object, param);
}
}catch(Exception e){
logger.error("queryForObject exception!",e);
throw new RuntimeException(e);
}
}else {
//获取实例失败,这里抛出异常
throw new RuntimeException("build result object failed!");
}

return object;
}

/**
* 根据传入Map获取查询参数数组对象
* @param params 参数Map
* @return Object[]
*/
private Object[] getQueryObject(Map<String,Object> params){
// 查询对象,存储查询条件数组
Object[] queryObject = new Object[params.size()];

// 获取查询参数
int index = 0;
// 初始化查询对象
for(String param : params.keySet()){
queryObject[index++] = params.get(param);
}

return queryObject;
}

/**
* 组装查询语句
* <p>
* 目前只支持了全部列查询
* 关于排序列(可变长度参数部分):
* @see queryForListOrderRow
* </p>
* @param rows 需要查找的列,如果null,则全字段
* @param params 参数map
* @param offset 偏移量,-1标示不分页
* @param pagesize 分页大小,-1标示不分页
* @param useValue 是否使用参数值作为搜索条件的值,若false,则使用?代替
* @return string 查询sql
*/
private String buildQuerySql(List<String> rows,Map<String,Object> params,int offset,int pagesize,
boolean useValue,Map<String,String>... orders){
// 获取需要查找的列
String listStr = getSelecteKeyFromList(rows);
// 组装sql
String sql = SQL_SELECT+listStr+ SQL_FROM + tableName;

//如果后面不含有查询条件,则直接返回
if(null!=params && 0 < params.size()){
sql = getSqlCondition(sql,params, useValue);
}

if(-1 != offset || -1 != pagesize){
// limit offset,pagesize
sql += (SQL_LIMIT+offset+SQL_SPLIT+pagesize+" ");
}

return getSqlOrder(sql,orders);
}

/**
* 组装根据条件查询总记录数的sql
* @param params 条件
* @return sql语句
*/
private String buildQueryConteSql(Map<String,Object> params){
// 获取需要查找的列
String sql = SQL_SELECT+SQL_COUNT+SQL_FROM+ tableName;

//如果后面不含有查询条件,则直接返回
if(null!=params && 0 < params.size()){
sql = getSqlCondition(sql,params, false);
}

return sql;
}

/**
* 从查询列的列表中取出列名,并按照sql的要求逗号隔开,如 a,b,c,d
* 如果传入查询列的列表为空,或者长度为0,怎返回 * ,代表全部字段都查询
* @param rows
* @return
*/
private static String getSelecteKeyFromList(List<String> rows){
if(null == rows || 0 == rows.size()){
return "*";
}
String listStr = rows.toString();
listStr = getSubString(listStr, 1, 1);
return listStr;
}

/**
* 组装更新语句
* 除了主键外的全字段更新,限制条件可以自由设定
* @param object
* @param useValue
* @param conditions
* @return
*/
private String buildUpdateObjectSql(Object object,boolean useValue,Map<String,Object> conditions){
//组装更新sql
String sql = SQL_UPDATE + tableName + SQL_SET;

//获取域数组
Field[] fields = getFieldFromObject(object);

//获取属性和值的map,不包括主键和序列ID
Map<String, Object> params = getParamMap(object, fields);

//更新sql必须含有需要修改的字段的内容,否则抛出异常
if(null == params || 0 >= params.size()){
throw new RuntimeException("can not update without values!");
}

//添加修改字段的sql片段
for(String key : params.keySet()){
sql += (key + SQL_EQUAL +(useValue? params.get(key):SQL_PLACE_HOLDER));
sql += SQL_SPLIT;
}

sql = getSubString(sql,0,1);//去掉最后的一个逗号

return getSqlCondition(sql,conditions, useValue);
}

/**
* 获取sql语句的后半部分,从where开始的条件设定部分
* @param sql sql前半部分,可以是select或者update或者delte,不限
* @param conditions 条件map
* @param useValue 是否使用具体值替代占位符?
* @return
*/
private static String getSqlCondition(String sql,Map<String,Object> conditions,boolean useValue){
//开始组装条件
sql += SQL_WHERE;

for(String key : conditions.keySet()){
sql += (key+SQL_EQUAL);
sql += useValue? conditions.get(key):SQL_PLACE_HOLDER;
sql += SQL_AND;
}

return getSubString(sql,0, 5);//取消最后的" and ";
}

/**
* 获取sql的后半order by的部分,如果没有指定排序,那么直接返回sql
* @param sql sql前半部分,通常截止于where语句
* @param orders 排序列的map,按照排序的次序依次排列
* @return 转换后的sql
*/
private static String getSqlOrder(String sql,Map<String,String>... orders){
if(null == orders || 0 == orders.length){
return sql;
}else {
sql += SQL_ORDER_BY;
}

//遍历order组,添加附加的排序条件
for(int i = 0; i < orders.length; i++){
//获取其中的每一组,并获取其中的key和value
Map<String,String> orderItem = orders[i];
if(!orderItem.keySet().isEmpty()){
//使用迭代器取出map中的第一个键值对,通常也是当前map中的唯一一对
Iterator<String> index = orderItem.keySet().iterator();
if(index.hasNext()){
String key = index.next();
sql += (" " + key + " "+orderItem.get(key));//like name desc
sql += SQL_SPLIT;//like name desc,
}
}
}
int splitPoint = sql.lastIndexOf(',');
sql = getSubString(sql,0, sql.length()-splitPoint);//去掉最后一个逗号

return sql;
}

/**
* 获取部分字符串
* @param str
* @param begin 起始位数,从首位开始计数,0开始
* @param end 从末尾开始计数的,截止位
* @return
*/
private static String getSubString(String str,int begin,int end){
return str.substring(begin, str.length()-end);
}

/**
* 从对象中获取Field数组
* @param object
* @return
*/
private Field[] getFieldFromObject(Object object){
@SuppressWarnings("rawtypes")
Class objectclass = object.getClass();
Field[] fields = getFieldsFromClass(objectclass);
return fields;
}

/**
* 获取结果的map<fieldName,fieldValue>
* <p>
* 从数据库中获取而来的ResultSet,类型为 <数据库字段相同大小写的字符串,数据库字段类型>
* 我们需要转换成:<对象中的属性名称大小写的字符串,对象中的属性的类型>
* </p>
* @param resultSet 结果set
* @param fields 属性数组
* @throws Exception
*/
private Map<String,? extends Object> getResultMapFromResultSet(ResultSet resultSet,Field[] fields) throws Exception{
//~~~ return value
Map<String,Object> map = new HashMap<String,Object>();

//获取元数据
ResultSetMetaData setMetaData = resultSet.getMetaData();
//遍历各列,获取各列的值和在DO类中对应的属性的名称
for(int i=1;i<=setMetaData.getColumnCount();i++){
//获得列名称
String columnName = setMetaData.getColumnName(i);//特别注意,这里的下标从1开始
if(logger.isDebugEnabled()){
logger.debug("get column:"+columnName);
}

//获得当前列的值
Object rowObject = resultSet.getObject(columnName) == null ? "":resultSet.getObject(columnName);

//获取当前列对应的属性在属性组中的下标
int index = getFieldIndexOfObject(fields,columnName);
if(-1 == index){
throw new Exception("can not find index of column :"+columnName+" in fields.");
}

//将当前字段从数据库类型转换成DO中的类型
rowObject = ConvertUtils.convert(rowObject.toString(), fields[index].getType());

//获得当前列在对象中对应的属性名
String realName = getFieldRealNameOfObject(fields,columnName);
if(null == realName || "".equals(realName)){
logger.error("no field match the name:"+columnName);
throw new Exception();
}

map.put(realName, rowObject);
}

return map;
}

/**
* 用于获取fields中当前数据库列对应的名字
* @param fields 属性域数组
* @param rowNameInResultSet
* @return
*/
private String getFieldRealNameOfObject(Field[] fields,String rowNameInResultSet){
List<String> fieldNameSet = getFiledNameList(fields);
//一次遍历,不分大小写,用于解决DO和数据库字段的属性名字大小写不一致的问题
for(String s:fieldNameSet){
if(StringUtils.equalsIgnoreCase(s, rowNameInResultSet)){
return s;
}
}
return null;
}
/**
* 返回当前列对应的属性在field数组中的下标
* @param fields
* @param rowNameInResultSet
* @return
*/
private int getFieldIndexOfObject(Field[] fields,String rowNameInResultSet){
List<String> fieldNameSet = getFiledNameList(fields);
//一次遍历,不分大小写,用于解决DO和数据库字段的属性名字大小写不一致的问题
int index = 0;
for(String s:fieldNameSet){
if(StringUtils.equalsIgnoreCase(s, rowNameInResultSet)){
return index;
}
index++;
}
return -1;
}

/**
* 根据class获取一个实例对象
* @param class
* @return null 或者对象实例
*/
private Object getInstanceByClass(@SuppressWarnings("rawtypes") Class className){
Object object = null;
try {
object = className.newInstance();//new 一个对象

//把产生的异常都吃掉,这里返回null可以触发上层抛出异常
} catch (InstantiationException e) {
logger.error("new instance exception!",e);
} catch (IllegalAccessException e) {
logger.error("can not access the instance method!",e);
}
//return null or a object
return object;
}

/**
* 根据类名获取所有声明的属性列表
* 注意其中也包含了不属于数据库对应字段的属性,比如序列号ID
* @param className 类名称
* @return field数组
*/
private Field[] getFieldsFromClass(@SuppressWarnings("rawtypes") Class className){
Field[] fields = className.getDeclaredFields();
return fields;
}

/**
* 根据属性数组获取属性名称的set
* 这里必须使用list,以保持次序
* @param fields
* @return
*/
private List<String> getFiledNameList(Field[] fields) {
//~~~ return value
List<String> fieldNameSet = new ArrayList<String>();

// get the name of each
for (int i = 0; i < fields.length; i++) {
fieldNameSet.add(fields[i].getName());
}
return fieldNameSet;
}
/**
* 设置需要增加的属性以及主键
* 针对于insert方法,提供需要插入的字段名称,其中不包括主键ID和序列号SerivalVserionId
* @param fieldNameSet
*/
private void setUsingListFromNameSet(List<String> fieldNameSet) {
//~~~ return value
List<String> usingColums = new ArrayList<String>();

// 使用所有的除了主键和序列号的字段名称
for (String field : fieldNameSet) {
if (StringUtils.equalsIgnoreCase(field,
DEFAULT_SERIALVERSIONUID_NAME)) {
//序列号掠过
continue;
}
if (!StringUtils.equalsIgnoreCase(field, DEFAULT_ID_NAME)) {
//不是主键,放入字段列表
usingColums.add(field);
} else {
//主键作为返回字段
inserActor = inserActor.usingGeneratedKeyColumns(field);
}
}
inserActor.setColumnNames(usingColums);
}

/**
* 检查是否是序列号ID
* @param field
* @return
*/
private boolean checkIsSerivalVersioinUID(String field){
return StringUtils.equalsIgnoreCase(field,DEFAULT_SERIALVERSIONUID_NAME);
}
/**
* 获取插入数据库中的sql需要的参数Map
* 其中不包括主键和序列号
* @param object 实例化后的对象
* @param fields 对象类包含的class
* @return <属性名称,属性值>
* @throws SecurityException
*/
private Map<String, Object> getParamMap(Object object, Field[] fields) throws SecurityException{
//~~~ return value
Map<String, Object> paramMap = new HashMap<String, Object>();
//依次遍历对象的get方法,获取对象的属性值,放入map中
for (int i = 0; i < fields.length; i++) {
if (!checkIsNeedField(fields[i].getName())) {
continue;
} else {
String methodName = getGetMethod(fields[i].getName());
if(logger.isDebugEnabled()){
logger.debug("run method:" + methodName);
}
try {
// 通过代理执行访问器
Method method = object.getClass().getMethod(methodName); // get方法没有参数,不需要获取参数类型,fields[i].getGenericType().getClass()
Object result = method.invoke(object, new Object[] {});//调用get方法获取属性值
String value = null==result? "":result.toString();
paramMap.put(fields[i].getName(), value);

} catch (SecurityException e) {
// 由安全管理器抛出的异常,指示存在安全侵犯
throw e;
} catch (NoSuchMethodException e) {
// 没有该方法
logger.error("There is no method '"+methodName+"' in Class "+object.getClass()+"! Check your getter method name!",e);
return null;
}catch (IllegalArgumentException e) {
// 参数不正确
logger.error("IllegalArgumentException happend because the method '"+methodName+"'"+"' in Class "+object.getClass()+ "has some parameters!",e);
return null;
} catch (IllegalAccessException e) {
// 访问的方法不能被访问,一般是由于private等权限问题
logger.error("The method '"+methodName+"' in Class "+object.getClass()+"can not be accessed!Check your getter method whether it is private of protected!",e);
return null;
} catch (InvocationTargetException e) {
// 映射目标异常
logger.error("The invoke method '"+methodName+"' in Class "+object.getClass()+"is not right!Check your getter method name!",e);
return null;
}
}
}
return paramMap;
}

/**
* 获取默认的get方法的名字,根据spring的默认规则
* @param fieldName
* @return
*/
private String getGetMethod(String fieldName){
//生成诸如getParam1的方法名称
String methodName = GET_METHOD_PRE + changeFirstChar2upper(fieldName);
return methodName;
}

private String getSetMethod(String fieldName){
//生成诸如setParam1的方法名称
String methodName = SET_METHOD_PRE + changeFirstChar2upper(fieldName);
return methodName;
}
/**
* 将字符串的首字母大写
* @param fieldName
* @return
*/
private static String changeFirstChar2upper(String fieldName){
return fieldName.toUpperCase().charAt(0) + fieldName.substring(1, fieldName.length());
}

/**
* 检查是否是必要的需要修改的字段
* @param fieldName
* @return
*/
private boolean checkIsNeedField(String fieldName){
if (StringUtils.equalsIgnoreCase(fieldName, DEFAULT_SERIALVERSIONUID_NAME)
|| StringUtils.equalsIgnoreCase(fieldName, DEFAULT_ID_NAME)) {
return false;
}
return true;
}
/***************************************///getter & setter //***********************************************
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}


可以看到,正如我之前所讲,这几种不同的查询全部依赖于底层的几个基础方法:

封装查询sql的方法:buildQuerySql

查询对象列表方法:queryForList

查询map列表方法:queryForMapList 即部分属性查询方法

通过这几个方法的组合以及可变长度参数的使用,可以灵活封装不同的api。

----------------------------------------------

从下一章节,我们开始讲述 修改 相关的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐