再上一篇文章
[框架那点事儿-快速开发季]编写自己的数据持久层(2)
主要讲了针对于查询API的实现,其中包括三个API:
// 查询单个对象
public Object queryForObject
//查询对象列表
public List queryForObjectList
//查询对象列表,根据指定字段排序
public List queryForObjectListOrderRow
在日常开发过程中,我们需要的查询可能不止这简单的三种,查询DO对象是一种方面的方式,另外的,对于其中部分属性的查询,上面的API虽然能够满足,但是获取到的DO对象中,部分字段有值,部分字段废弃,是不友好的做法,对于使用者而言,需要过多的关注,哪些字段有值,哪些字段使用的时候会得到空值,甚至会引发NPE。
所以,这一节,我们讲解针对于部分属性的查询,引入以下几个API:
/** * 查询部分字段 * <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); }
这几个方法都很简单,最终会调用 getJdbcTemplate().query 进行具体查询,我们需要封装的有两个地方:
1、查询sql的自动生成。
这样的目的是隔离使用者频繁编写sql,导致代码维护困难,java代码中到处是sql,是一种不友好的现象。
2、查询结果的封装,也就是重写rowMapper。
这样做的目的减少这部分体力活的工作量,同时提高代码的重用性。
下面是具体的代码,展示具体实现过程:
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 属性名字 大小写无所谓 * @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); } /*****************************///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; } }
部分代码与之前版本有所出入,请以此重构后的版本为主。