您的位置:首页 > 其它

[框架那点事儿-快速开发季]编写自己的数据持久层(6)思考

2010-07-30 16:44 519 查看
这一章题目是思考,是因为工作进行到现在,我开始反思着一些API,到底能覆盖多少的日常工作,带来多少的便捷。从最开始的插入开始,到现在已经完成了根据sql来实现分页查询,我发现用这种纯面向对象的API封装思想去开展我的工作已经无法满足自己大脑中能思考到的使用场景。

比如目前我们的查询API,其参数形式都是默认采用AND的关系,同时由于采用的是Map来保存参数,导致无法使用多个同样参数的条件,比如下面这2条sql:

select id from person where age=25 or age =27;

select id from person where age=25 and age=27;

他们有两个查询条件,但是却只有一个age列作为查询一句,同时对于参数也是具有两个不同的值,并且,一个是AND的关系,另一个是OR的关系,使用我们封装的API,将无法满足这样的需求。那么该怎么办呢?我想过很多种方案,但是直到现在,我依旧能想出新的应用场景,导致上一种方案无法满足需求。一种极端的思想:

Map<Map<String,Object>,OperateEnum> 其中的key值是具体的条件键值对,后面的是当前的条件与其他的条件是什么关系的枚举。

然后组装sql的时候可以使用上述的内容解析。但是,下面一种情况呢:

SELECT * FROM PERSON WHERE (AGE=25 OR AGE=26) AND STATUS=1;

我们知道在同时具有or和and的表达式中,表达or关系的语句必须要包含其来,否则很容易出现关系结合错位。

当这种情况出现的时候,即便是那种极端的方案我们还是一筹莫展。该怎么办呢?曾经我一度陷入到这个问题中,于是去看hibernate,看ibatis,发现hibernate提供了非常复杂的API,纵然解决了这个问题,但是对于使用者而言,并没有减少工作反而增加不少,而且很容易出错。再看看ibatis,这个问题很容易的被解决是因为他使用的是映射文件的形式,其中不过是些sql,所以这个问题对其而言,迎刃而解。那么我们该怎么做,是学习hibernate去封装一堆的API出来,让使用者不断的采用add的方法去添加后缀,然后得到一个并不复杂的sql,然后执行?

这样的工作是无意义的,我们的目的是降低工作量,而不是增加复杂度和使用门槛。所以无奈之下,我想还是沿用ibatis的思想吧,就是接收sql,就让使用者来告诉我,这个sql是什么样子吧!

所以我们接纳合法的sql:

select * from table where param1=? and (param2=? or param3=?) order by param4 desc;

这样的一条sql,使用了占位符的形式,对于使用者而言并不困难。

而对于只查询一条结果的情况,干脆就直接使用具体的值来替代占位符吧,这样省去了使用者使用Map<String,Object>的麻烦。

那么我们就需要提供支持使用sql来进行数据库操作的API,增加以下几个API:

/**
* 根据自定义sql查询对象,主要封装了rowMapper的转换过程
* 要求:遵循spring风格的getter和setter方法
* 		    属性的名称和数据库的名称要对应,大小写不区分
* 		    这里一定要注意,如果属性的大小写有点个性非常强的个人色彩,
* 		    那么相应的setter方法要保证'set'后的属性首字母大写,其他字母大小写保持不变
* 		   如:pArAm ---->  getPArAm  &  setPArAm
* 使用示例:
* -----------------------------------------------------
* String sql = "select * from Person where name='name' and (address='d1' or address='d2') and type=2;
* Person p = (Person) queryForObjectBySql(sql,Person.class);
*
* @param sql 自定义sql
* @param returnClass 返回类型
* @return 封装好的对象
*/
public Object queryForObjectBySql(String sql,@SuppressWarnings("rawtypes") final Class returnClass){
return queryForObject(sql,returnClass);
}


/**
* 根据sql查询对象列表,全字段查询并封装成特定类型
* <p>
* 	本方法服务于那些自定义sql进行查询的需求。上述的方法中的查询条件皆为普通的and关系,这个方法可以自定义任何类型的sql
* 	方法主要封装了rowMapper的转换过程。
*  sql中查询参数使用占位符。
* </p>
* @param sql 查询sql
* @param params 参数
* @param returnClass 返回类型 List<? extends Object>
* @return
*/
@SuppressWarnings("rawtypes")
public List queryForObjectListBySql(String sql,Map<String,Object> params,Class returnClass){
if(logger.isDebugEnabled()){
logger.debug("RUN SQL:"+sql);
}

//执行查询
return queryForList(sql,null,returnClass);
}


* 根据指定sql查询部分字段
* @param sql 查询sql,参数使用占位符
* @param params 查询参数
* @param objectClass 字段所在Do类型
* @return List<Map<String,Object>>
*/
public List<Map<String,Object>> queryFieldsListBySql(String sql,Map<String,Object> params,@SuppressWarnings("rawtypes")final Class objectClass){
return queryForMapList(sql,null,objectClass);
}


/**
* 根据sql自动分页查询
* 传入的sql不需要关注分页内容,只需要关注于普通查询部分,方法内部自动实现分页查询
* 返回封装好的分页器
* 使用示例
* --------------------------------------------------------------
* String sql = "select * from Person where (name='quzishen' or name='zishenqu') and age=25";
*
* Paginal p = queryObjectListForPagingBySql(sql,Person.class,1,10);
* ...
* @param sql
* @param objectClass
* @param pageNomber
* @param pageSize
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Paginal queryObjectListForPagingBySql(String sql,final Class objectClass,int pageNomber,int pageSize){
//~~~ return value
Paginal<? extends Object> paginal = getPaginalBySql(sql,pageNomber,pageSize);

//自动添加分页部分
sql = getPagingSql(sql, paginal.getOffset(), paginal.getPageSize());

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

return paginal;
}


/**
* 根据指定sql查询部分参数
* @param sql
* @param objectClass
* @param pageNomber
* @param pageSize
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Paginal queryFieldsListForPagingbySql(String sql,final Class objectClass,int pageNomber,int pageSize){
//~~~ return value
Paginal<? extends Object> paginal = getPaginalBySql(sql,pageNomber,pageSize);

//自动添加分页部分
sql = getPagingSql(sql, paginal.getOffset(), paginal.getPageSize());

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

return paginal;
}


/**
* 通过sql语句查询结果记录数
* @param sql 查询sql
* @return 记录数
*/
public int queryCount(String sql){
sql = getCountSqlFromSelectSql(sql);
//查询总记录数
int totalCount = getJdbcTemplate().queryForInt(sql);

return totalCount;
}


这几个API,支持了sql作为参数直接进行数据库操作。而之前的那几个api,是我一直很希望能达到的一种结果,既不要像ibatis这样使用配置文件编写sql,导致映射文件和java文件同时编写增加出错率,也不要像hibernate那样api使用起来需要特殊学习。那他们的定位就是作为便捷方法吧,虽然他们的功能,上面这几个方法也能实现,但是在使用上,他们并不需要编写具体的sql,只需要像操作API一样的调用方法,传入参数,拿到结果就可以了。

工作进行到目前为止,这个过程中,在进行工作前很多的没想到的问题都已经逐渐清晰,比如封装的过程中,会遇到哪些问题,需要注意哪些问题,这里可以简单的总结一下:

1、封装的api的类型,增删查改只是一个笼统是说法,单纯就 查 而言,就可以细化成无数种,比如使用单个字段查,使用多个字段联合查,联合查的字段的关系是AND还是OR还是BETWEEN还是同时存在,比如分页查询某列,分页查询对象,排序查询等等等等。增加可以分成单个对象增加,批量增加等等。难倒这些都要去通过封装提供吗?对于查询的分类而言,是无法通过一个又一个api去满足,然后提供给使用者一堆的API,让他们去费劲脑汁的选择,这样是得不偿失的,不能单纯的为了封装而封装。而如果要达到一个简练的封装,是否要提供一个解析算法,动态的解析使用者到底要什么样的sql?这样该如何去设计,至少现在我还没想好。

2、对于数据库类型和DO类中类型的转换问题,是使用一个Object笼统的概括掉,还是使用? extends Object更精准的定位,对于一个api的返回值,在多种应用场景下,是否满足大多数的而且是便捷而友好的?

3、该如何去选择jdbcTemplate提供的方法来实现我们的功能,是query还是queryForList,是update,还是batchUpdate,这些都是要在特定的api中具体思考的。

4、整个抽象类的设计上,抽象出来的原子方法是否可以为不同的包装api服务?比如类中queryForList和queryForMap两个方法,还有rowMapper的转换方法,我们抽象出来以后,向上封装了多种多样的api,虽然底层都是这几个基础服务,但是对外丰富的封装手段将使得我们的父类功能完善多种多样。

5、这个封装的过程,到底为了什么,我们将是为了提高开发效率,降低编码工作量,所以在设计api的时候,其输入输出一定要保证简捷,易用,至少目前虽然采用了@Table的注解方法解决获取表名称的问题,还是会想是否有更好的方法,比如外部传入对象类型的问题等等。

就让这个过程当做是自己的学习和修炼吧,以后再次遇到相同的工作,多想想这一次的经历,我得到了什么样的经验,遇到了哪些问题,这或许才是本次工作最重要的收获吧!

最后附上目前为止的整个类的内容://后续将继续封装。。。。

package com.netease.space.dao.base;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
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.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
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;
//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";
//default max size of each insert
public int insertPageSize = 500;

//~~~ 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{
if(null == object){
throw new RuntimeException("can not inser a null value!");
}
// 获取类型
@SuppressWarnings("rawtypes")
Class objectClass = object.getClass();
// 使用SimpleJdbcInsert完成插入的动作
SimpleJdbcInsert inserActor = getSimpleJdbcInsert(objectClass);
// 获取属性数组
Field[] fields = getFieldsFromClass(objectClass);
// 获取插入数据库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{
//获取表名
tableName = getTableConfigName(returnClass);
// 组装查询sql,参数使用占位符
String sql = buildQuerySql(null,params,-1,-1,true);

return queryForObjectBySql(sql,returnClass);
}

/**
* 根据自定义sql查询对象,主要封装了rowMapper的转换过程
* 要求:遵循spring风格的getter和setter方法
* 属性的名称和数据库的名称要对应,大小写不区分
* 这里一定要注意,如果属性的大小写有点个性非常强的个人色彩,
* 那么相应的setter方法要保证'set'后的属性首字母大写,其他字母大小写保持不变
* 如:pArAm ----> getPArAm & setPArAm
* 使用示例:
* -----------------------------------------------------
* String sql = "select * from Person where name='name' and (address='d1' or address='d2') and type=2;
* Person p = (Person) queryForObjectBySql(sql,Person.class);
*
* @param sql 自定义sql
* @param returnClass 返回类型
* @return 封装好的对象
*/
public Object queryForObjectBySql(String sql,@SuppressWarnings("rawtypes") final Class returnClass){
return queryForObject(sql,returnClass);
}

/**
* 查询对象列表结果
* <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){
//获取表名
tableName = getTableConfigName(returnClass);
// 组装查询sql,参数使用占位符替代
String sql = buildQuerySql(null,params,-1,-1,false,orders);
if(logger.isDebugEnabled()){
logger.debug("RUN SQL:"+sql);
}
//执行查询
return queryForList(sql,params,returnClass);
}

/**
* 根据sql查询对象列表,全字段查询并封装成特定类型
* <p>
* 本方法服务于那些自定义sql进行查询的需求。上述的方法中的查询条件皆为普通的and关系,这个方法可以自定义任何类型的sql
* 方法主要封装了rowMapper的转换过程。
* sql中查询参数使用占位符。
* </p>
* @param sql 查询sql
* @param params 参数
* @param returnClass 返回类型 List<? extends Object>
* @return
*/
@SuppressWarnings("rawtypes")
public List queryForObjectListBySql(String sql,Map<String,Object> params,Class returnClass){
if(logger.isDebugEnabled()){
logger.debug("RUN SQL:"+sql);
}

//执行查询
return queryForList(sql,null,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保存每一列的结果
*/
public List<Map<String,Object>> queryFieldsListOrderRow(@SuppressWarnings("rawtypes")final Class objectClass,
Map<String,Object> params,List<String> returnFields,Map<String,String>... orders){
//获取表名
tableName = getTableConfigName(objectClass);
// 组装查询sql,参数使用占位符替代
String sql = buildQuerySql(returnFields,params,-1,-1,false,orders);
return queryForMapList(sql,params,objectClass);
}

/**
* 根据指定sql查询部分字段
* @param sql 查询sql,参数使用占位符
* @param params 查询参数
* @param objectClass 字段所在Do类型
* @return List<Map<String,Object>>
*/
public List<Map<String,Object>> queryFieldsListBySql(String sql,Map<String,Object> params,@SuppressWarnings("rawtypes")final Class objectClass){
return queryForMapList(sql,null,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,paginal.getPageSize());//总页码

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

return paginal;
}

/** * 根据sql自动分页查询 * 传入的sql不需要关注分页内容,只需要关注于普通查询部分,方法内部自动实现分页查询 * 返回封装好的分页器 * 使用示例 * -------------------------------------------------------------- * String sql = "select * from Person where (name='quzishen' or name='zishenqu') and age=25"; * * Paginal p = queryObjectListForPagingBySql(sql,Person.class,1,10); * ... * @param sql * @param objectClass * @param pageNomber * @param pageSize * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Paginal queryObjectListForPagingBySql(String sql,final Class objectClass,int pageNomber,int pageSize){ //~~~ return value Paginal<? extends Object> paginal = getPaginalBySql(sql,pageNomber,pageSize); //自动添加分页部分 sql = getPagingSql(sql, paginal.getOffset(), paginal.getPageSize()); // 执行查询 List resultList = queryForList(sql,null,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;
}

/** * 根据指定sql查询部分参数 * @param sql * @param objectClass * @param pageNomber * @param pageSize * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Paginal queryFieldsListForPagingbySql(String sql,final Class objectClass,int pageNomber,int pageSize){ //~~~ return value Paginal<? extends Object> paginal = getPaginalBySql(sql,pageNomber,pageSize); //自动添加分页部分 sql = getPagingSql(sql, paginal.getOffset(), paginal.getPageSize()); // 执行查询 List resultList = queryForMapList(sql,null,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;
}

/**
* 通过sql语句查询结果记录数
* @param sql 查询sql
* @return 记录数
*/
public int queryCount(String sql){
sql = getCountSqlFromSelectSql(sql);
//查询总记录数
int totalCount = getJdbcTemplate().queryForInt(sql);

return totalCount;
}
/**
* 从select查询语句获取查询count的语句
* @param sql
* @return
*/
private static String getCountSqlFromSelectSql(String sql){
int selectIndex = sql.indexOf(SQL_SELECT.trim());
String pre = StringUtils.substring(sql, 0, selectIndex+7);
int fromIndex = sql.indexOf(SQL_FROM.trim());
String fix = StringUtils.substring(sql, fromIndex, sql.length());

return pre + SQL_COUNT + fix;
}

/**
* 根据查询条件修改记录
* 除了ID之外的全字段修改,适用于从数据库取出的对象的修改。不适合单字段或部分字段修改。
* <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");
*
* Map<String,Object> conditions = new HashMap<String,Object>();
* conditions.put("ID","1");
*
* long id = updateObject(person);
*
* </p>
* @param 修改后的对象值
* @param 查询条件,可以单独只有一个ID
* @return
* @throws RuntimeException
*/
public int updateObject(Object object,Map<String,Object> conditions) throws RuntimeException{
if(null == object){
throw new RuntimeException("can not insert a null value!");
}
//获取表名
tableName = getTableConfigName(object.getClass());
//获取sql
String sql = buildUpdateObjectSql(object,true,conditions);
//执行更新
return getJdbcTemplate().update(sql);
}

/**
* 根据查询条件更新部分字段
* <p>
* 适用于更新部分字段情况
* 使用示例:
* -----------------------------------------------------
* Map<String,Object> params = new HashMap<String,Object>();
* params.put("status","expired");
*
* Map<String,Object> conditions = new HashMap<String,Object>();
* conditions.put("status","use");
*
* updateField(params,conditions,Person.class);
*
* </p>
* @param params 需要更新的字段以及值
* @param conditions 查询条件
* @param objectClass 字段所在的DO类的类型
* @return
* @throws RuntimeException
*/
public int updateField(Map<String,Object> params,Map<String,Object> conditions,@SuppressWarnings("rawtypes") Class objectClass) throws RuntimeException{
if(null == params || 0 == params.size()){
throw new RuntimeException("can not insert a null value!");
}

//获取表名
tableName = getTableConfigName(objectClass);
//获取sql
String sql = buildUpdateFieldSql(params, objectClass, true, conditions);

//执行更新
return getJdbcTemplate().update(sql);
}
/**
* 批量插入
* <p>
* 批量将对象列表插入到数据库中,主键自动生成。
* 可以选择是否使用分页插入模式,当数据量比较大的时候可以选择,以提升性能;
* · 开启分页插入模式的条件: 1、moreThanOnce 设置 true 。 2、列表总长度大于单次最大插入数目,默认500,可修改。
*
* 使用示例
* -------------------------------------------------------
* List<Person> addlist = new ArrayList<Person>();
* Person p1 = new Person();
* p1.init(...);
* Person p2 = new Person();
* p2.init(...);
*
* addlist.add(p1); addlist.add(p2);
*
* batchInsert(addlist,Person.class);
* ...
* </p>
* @param updateList 插入列表
* @param objectClass 列表对象类型
* @throws RuntimeException
*/
@SuppressWarnings("rawtypes")
public void batchInsert(List<? extends Object> updateList,Class objectClass,boolean moreThanOnce) throws RuntimeException{
if(null == updateList || 0 == updateList.size()){
throw new RuntimeException("can not inser null value!");
}
// 获取SimpleJdbcInsert
SimpleJdbcInsert inserActor = getSimpleJdbcInsert(objectClass);

// 如果选择批量插入,并且列表长度大于单次插入最大值,则使用分页插入
if(moreThanOnce && insertPageSize < updateList.size()){
// 需要的分页总数
int totalPage = (0==updateList.size() / insertPageSize) ?
(updateList.size() / insertPageSize) : (updateList.size() / insertPageSize+1);
// 开始分页插入
for(int pageNo=1;pageNo<=totalPage;pageNo++){
int fromIndex = 0;
int endIndex = updateList.size() > insertPageSize ? insertPageSize : updateList.size();
List<? extends Object> subList = updateList.subList(fromIndex, endIndex);//[)
//insert into db
insertSqlParameterSourceInfoDB(subList,inserActor);
}
}else {
// 不分页直接插入
insertSqlParameterSourceInfoDB(updateList,inserActor);
}
}

/**
* 根据ID批量更新
* <p>
* 使用示例
* --------------------------------------------------------------------------------
* class Person{
* ①、private static final long serialVersionUID = 4311010571442992834L;//位置可以在这里
* private long id;
* ②、private static final long serialVersionUID = 4311010571442992834L;//位置可以在这里
* private String name;
* ③、private static final long serialVersionUID = 4311010571442992834L;//位置可以在这里
* }
* //上述类过滤后的顺序为 id - name
*
* updateList.add(p1);
* updateList.add(p2);
* updateList.add(p3);
*
* batchUpdate(updateList,Person.class);
* ...
* </p>
* @param updateList 更新列表
* @param objectClass 更新对象类型
* @throws RuntimeException
*/
@SuppressWarnings("rawtypes")
public void batchUpdate(final List updateList,Class objectClass) throws RuntimeException{
if(null == updateList || 0 == updateList.size()){
throw new RuntimeException("can not inser null value!");
}
// 获取一个对象
Object object = getInstanceByClass(objectClass);
// 生成更新sql,update table set .x = ?.. where id = ?
Map<String,Object> conditions = new HashMap<String,Object>();
conditions.put(DEFAULT_ID_NAME, SQL_PLACE_HOLDER);
//获取表名
tableName = getTableConfigName(object.getClass());
String sql = buildUpdateObjectSql(object, false, conditions);
// 获取属性数组,其中不包括数据库中不含有的字段 serivalversionUID
final Field[] fields = getFieldsMatchedDBFromClass(objectClass,false);

/**
* 执行批量更新操作
* 生成的sql中的占位符的顺序和DO类中顺序固定,所以这里也要按照DO类中的顺序来实施
* 在Fields中包含了部分与数据库不匹配的字段,有serivalversionuid.所以应该去除掉
*/
getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter(){

public int getBatchSize() {
return updateList.size();
}
// 设置参数
public void setValues(PreparedStatement preparedStatement, int index)
throws SQLException {
Object o = updateList.get(index);

for(Field field : fields){
//列从1开始计数,但是方法内部也从0开始计数,所以这里需要加1
int row =1+getFieldIndexOfObject(fields, field.getName());
String value = getValueByName(o,field.getName());
preparedStatement.setString(row, value);
}

// 设置最后的一位,即更新条件,此处为ID,sql中参数总数为fields.length,加1可以判断最后一个ID参数的位置
preparedStatement.setString(fields.length+1, getValueByName(o,DEFAULT_ID_NAME));
}
});
}
@SuppressWarnings("rawtypes")
public void batchUpdate(final List updateList,Map<String,Object> conditions,Class objectClass) throws RuntimeException{
if(null == updateList || 0 == updateList.size()){
throw new RuntimeException("can not inser null value!");
}
// 获取一个对象
Object object = getInstanceByClass(objectClass);
//获取表名
tableName = getTableConfigName(objectClass);
// 生成更新sql
String sql = buildUpdateObjectSql(object, false, conditions);
// 获取属性数组,其中不包括数据库中不含有的字段 serivalversionUID
final Field[] fields = getFieldsMatchedDBFromClass(objectClass,false);

/**
* 执行批量更新操作
* 生成的sql中的占位符的顺序和DO类中顺序固定,所以这里也要按照DO类中的顺序来实施
* 在Fields中包含了部分与数据库不匹配的字段,有serivalversionuid.所以应该去除掉
*/
getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter(){

public int getBatchSize() {
return updateList.size();
}
// 设置参数
public void setValues(PreparedStatement preparedStatement, int index)
throws SQLException {
Object o = updateList.get(index);

for(Field field : fields){
//列从1开始计数,但是方法内部也从0开始计数,所以这里需要加1
int row =1+getFieldIndexOfObject(fields, field.getName());
String value = getValueByName(o,field.getName());
preparedStatement.setString(row, value);
}
}
});
}

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

/**
* 获取SimpleJdbcInsert
* @param objectClass 插入对象的类型
* @return 初始化后的SimpleJdbcInsert实例
*/
private SimpleJdbcInsert getSimpleJdbcInsert(@SuppressWarnings("rawtypes") Class objectClass){
//获取表名
tableName = getTableConfigName(objectClass);
// 使用SimpleJdbcInsert完成插入的动作
SimpleJdbcInsert inserActor = new SimpleJdbcInsert(getJdbcTemplate()).withTableName(tableName);
// 获取该对象的所有的private protected publick的属性
Field[] fields = getFieldsFromClass(objectClass);
// 将属性名字保存下来
List<String> fieldNameSet = getFiledNameList(fields);
// 设置需要更新的字段以及主键
setUsingListFromNameSet(inserActor,fieldNameSet);

return inserActor;
}

/**
* 根据sql获取初始化的分页器
* @param sql 查询sql
* @param pageNomber 页号
* @param pageSize 分页大小
* @return
*/
private Paginal<? extends Object> getPaginalBySql(String sql,int pageNomber,int pageSize){
//~~~ return value
Paginal<? extends Object> paginal = new Paginal<Object>();

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

//获取查询总数的语句 将select 到 from中间部分替换成count(*)
//查询总记录数
int totalCount = queryCount(sql);

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

return paginal;
}

/**
* 将参数列表插入到数据库中
* @param updateList 插入列表
* @param inserActor 指定的插入执行器
*/
private void insertSqlParameterSourceInfoDB(List<? extends Object> updateList,SimpleJdbcInsert inserActor){
// 使用 SqlParameterSource 封装插入参数
SqlParameterSource[] paramSources = new SqlParameterSource[updateList.size()];
int index = 0;
for(Object o : updateList){
//依次遍历列表,获取插入对象,封装成SqlParameterSource并放入数组
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(o);
paramSources[index++] = paramSource;
}

//执行批量插入,注意这里的返回值是没有意义的,一般由底层jdbc决定,通常会返回-2
inserActor.executeBatch(paramSources);
}

/**
* 查询列表
* @param sql 查询sql,其中参数部分使用占位符?代替具体的值
* @param params 查询条件值的map
* @param returnClass 返回值类型
* @return 查询列表
*/
@SuppressWarnings("rawtypes")
private List queryForList(String sql,Map<String,Object> params,final Class returnClass){
if(null != params){
// 查询对象,存储查询条件数组
Object[] queryObject = getQueryObject(params);
//执行查询,设置rowMapper进行结果从List<Map> --> List<Object>的转换
return queryObjectListWithQueryObject(sql,queryObject,returnClass);
} else {
//执行查询,设置rowMapper进行结果从List<Map> --> List<Object>的转换
return queryObjectListWithoutObject(sql,returnClass);
}

}
/**
* 带参数的查询,适用于sql使用占位符的
* @param sql
* @param queryObject
* @param returnClass
* @return
*/
@SuppressWarnings("rawtypes")
private List queryObjectListWithQueryObject(String sql,Object[] queryObject,final Class returnClass){
//执行查询,设置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;
}
});
}

/**
* 不带参数的查询,适用于sql不使用占位符的
* @param sql
* @param returnClass
* @return
*/
@SuppressWarnings("rawtypes")
private List queryObjectListWithoutObject(String sql,final Class returnClass){
//执行查询,设置rowMapper进行结果从List<Map> --> List<Object>的转换
return getJdbcTemplate().query(sql,new RowMapper(){
public Object mapRow(ResultSet resultSet, int rowNum) throws SQLException {
//~~~ return value 组装返回对象
Object object = getObjectFromResultSet(resultSet,returnClass);

return object;
}
});
}

/**
* 查询map列表
* @param sql
* @param params
* @param objectClass
* @return
*/
@SuppressWarnings({ "rawtypes"})
private List<Map<String,Object>> queryForMapList(String sql,Map<String,Object> params,final Class objectClass){
if(null == params){
return queryForMapListWithoutObject(sql,objectClass);
}else {
//获取查询参数
Object[] queryObject = getQueryObject(params);
return queryForMapListWithObject(sql,queryObject,objectClass);
}
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private List<Map<String,Object>> queryForMapListWithObject(String sql,Object[] queryObject,final Class objectClass){
//获取对象声明的属性
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!");
}
}
});
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private List<Map<String,Object>> queryForMapListWithoutObject(String sql,final Class objectClass){
final Field[] fields = getFieldsFromClass(objectClass);
return (List<Map<String,Object>>)getJdbcTemplate().query(sql, 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;
}

/**
* 查询单一对象
* @param sql 执行sql ,使用占位符
* @param params 参数
* @param returnClass 返回类型
* @return
*/
private Object queryForObject(String sql,@SuppressWarnings("rawtypes") final Class returnClass){
//调用queryForObject并且重新RowMapper回调方法
return getJdbcTemplate().queryForObject(sql,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>
* 目前只支持了全部列查询
* 关于排序列(可变长度参数部分):
* @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);
}
// 获取排序部分
sql = getSqlOrder(sql,orders);
// 获取分页部分
sql = getPagingSql(sql,offset,pagesize);

return sql;
}
/**
* 获取sql的分页部分 limit x,y
* @param sql
* @param offset
* @param pagesize
* @return
*/
private static String getPagingSql(String sql,int offset,int pagesize){
if(-1 != offset || -1 != pagesize){
// limit offset,pagesize
sql += (SQL_LIMIT+offset+SQL_SPLIT+pagesize+" ");
}
return sql;
}

/**
* 组装根据条件查询总记录数的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 组装后的sql
*/
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的set部分
sql = getUpdateSqlSetPart(sql,params,object.getClass(),useValue);

//Be Attention!!!
return getSqlCondition(sql,conditions, useValue);
}

/**
* 组装更新部分字段sql
* @param updateFileds 需要更新的字段以及更新值
* @param objectClass 更新对象
* @param useValue 是否使用具体值替代占位符
* @param conditions 查询条件
* @return
*/
private String buildUpdateFieldSql(Map<String,Object> updateFileds,@SuppressWarnings("rawtypes") Class objectClass,boolean useValue,Map<String,Object> conditions){
//组装更新sql
String sql = SQL_UPDATE + tableName + SQL_SET;

//获取更新sql的set部分
sql = getUpdateSqlSetPart(sql,updateFileds,objectClass,useValue);

//Be Attention!!!
return getSqlCondition(sql,conditions, useValue);
}

/**
* 获取更新sql的set部分
* @param sql sql前半部分
* @param updateFileds 需要更新的字段
* @param objectClass 对象类型
* @param useValue 是否使用占位符
* @return
*/
private static String getUpdateSqlSetPart(String sql,Map<String,Object> updateFileds,@SuppressWarnings("rawtypes") Class objectClass,boolean useValue){
//获取域数组
Field[] fields = getFieldsFromClass(objectClass);

//更新sql必须含有需要修改的字段的内容,否则抛出异常
if(null == updateFileds || 0 >= updateFileds.size()){
throw new RuntimeException("can not update without values!");
}
//添加修改字段的sql片段,set的顺序和DO类中的顺是相同的,以便于后续batch操作
for(Field field : fields){
String rowName = field.getName();
if(updateFileds.containsKey(rowName)){
sql += (rowName + SQL_EQUAL +(useValue? (StringUtils.isBlank(updateFileds.get(rowName).toString())? "null":"'"+updateFileds.get(rowName)+"'"):SQL_PLACE_HOLDER));
sql += SQL_SPLIT;
}
}

int lastD = sql.lastIndexOf(',');
sql = getSubString(sql,0,sql.length()-lastD);//去掉最后的一个逗号

return sql;
}

/**
* 获取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){
if(null == conditions || 0 == conditions.size()){
return sql;
}
//开始组装条件
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 对象含有的filed数组
* @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 static Field[] getFieldsFromClass(@SuppressWarnings("rawtypes") Class className){
Field[] fields = className.getDeclaredFields();
return fields;
}

/**
* 根据类名获取除了SERIALVERSIONUID外的属性列表
* @param className 类名称
* @param isIncludeKeyId 是否包含id
* @return field数组
*/
private Field[] getFieldsMatchedDBFromClass(@SuppressWarnings("rawtypes") Class className,boolean isIncludeKeyId){
Field[] fields = getFieldsFromClass(className);
Field[] newfields = new Field[isIncludeKeyId? fields.length-1:fields.length-2];//如果不包含主键,则比原来少两位

int index = 0;
for(Field f : fields){
if(isIncludeKeyId? !checkIsSerivalVersioinUID(f.getName()):checkIsNeedField(f.getName())){
newfields[index++] = f;
}
}
return newfields;
}

/**
* 根据属性数组获取属性名称的set
* 这里必须使用list,以保持次序
* @param fields
* @return
*/
private static 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(SimpleJdbcInsert inserActor,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 value = getValueByName(object,fields[i].getName());
paramMap.put(fields[i].getName(), value);
}
}
return paramMap;
}

/**
* 根据名称获取对象中的值
* @param object 对象
* @param name 属性名
* @return
* @throws RuntimeException
*/
private String getValueByName(Object object,String name) throws RuntimeException{
String methodName = getGetMethod(name);
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();
return 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;
}
}

/**
* 获取默认的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;
}
}


注解方法:

package com.netease.space.dao.base;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 针对于数据库表的配置的注解
*
* 工程名称:NormandyPosition
* 类型名称:Table
* 概要:
* <p> 目前主要用于配置数据库表名 </p>
* 创建时间:2010-7-28 上午10:40:42
* 创建人:quzishen
* 最后修改时间:2010-7-28 上午10:40:42
* 最后修改内容:
* @version 1.0
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
/**~~~~ 表名*/
String tableName() default("table");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐