(新版增加了直接结果映射VO的功能无需再写rowcallbackhandler了,集成ehcache、memcached提高处理效率)
犹豫了很久,还是决定将自己的东西(sagacity睿智开发框架)的一部分逐步奉献给大家,首先申明一下版权问题,请注明“陈氏查询”!
进入正题:
首先介绍一下sagacity(睿智)框架:其包括快速原型的页面框架(这里面也有一个非常独到人性化的界面的设计),sagacity-core核心代码库提供针对企业应用开发所需要的工具类和组件,sagacity-toolkit 工具库,包括快速原型项目生成,excel导入工具(用于项目试运行阶段数据初始化);sagacity-struts:基于struts2+spring+hibernate+sitemesh+spring security技术以实际企业开发项目为背景非常实用演示项目。sagacity项目从实际项目出发,贯穿整个项目周期提供优雅的实现以及从开发人员的角度着眼简化开发难度!这里不一一而谈。
第一讲:陈氏查询
先抛一个问题:大家平时做数据库查询时查询条件很多也不定时怎么写hql以及sql语句?
比如:
queryStr.append("select * ");
queryStr.append("from OA_CAR_REGIST t ");
queryStr.append("where 1=1 ");
if(CarRegiestVO.isActive!=null)
queryStr.append(" and t.IS_ACTIVE is ? ");
if(CarRegiestVO.beginDate!=null && CarRegiestVO.endDate!=null)
queryStr.append(" and t.REGIST_DATE>=? and t.REGIST_DATE<= ? ");
if(CarRegiestVO.carMode!=null)
queryStr.append(" and t.CAR_MODE like "+carMode+"%");
sql结构非常混乱,从数据库客户端写好的sql语句还得用stringBuffer给拼接起来,以后要是改写一下语句还得debug程序得到最后的语句,太累了!
我们的做法:
我们的项目基于hibernate的,如下基于hibernate NamedQuery扩展。
<hibernate-mapping>
<sql-query name="hr_searchOrganInfo">
<![CDATA[
from HrOrganInfo
where 1=1
#[and createDate>=? and createDate<=?]
#[and ORGAN_NO like ?]
#[and ORGAN_NAME like ?]
#[and IS_ACTIVE=?]
]]>
</sql-query>
<sql-query name="hr_loadOrganTree">
<![CDATA[
select b.ORGAN_NO,b.ORGAN_NAME,b.PRE_ORGAN_NO,0 AS TYPE
from HR_ORGAN_INFO b
where 1=1
#[and b.ORGAN_NO=?]
#[and b.IS_ACTIVE=?]
union
select b.ORGAN_NO,b.ORGAN_NAME,b.PRE_ORGAN_NO,0 AS TYPE
from HR_ORGAN_INFO a,HR_ORGAN_INFO b
where 1=1
#[and a.ORGAN_NO=b.PRE_ORGAN_NO and (a.ORGAN_NO=? or b.PRE_ORGAN_NO=a.ORGAN_NO)]
#[and b.IS_ACTIVE=?]
]]>
</sql-query>
对于不定的查询条件以#[和]标记对,支持嵌套
我们的调用过程:
/**
* 分页方式查询机构信息
* @param organInfoVO
* @param pageModel
* @return
* @throws Exception
*/
public PaginationModel searchOrganInfo(OrganInfoVO organInfoVO,
PaginationModel pageModel) throws Exception {
return this.findPageByhql("hr_searchOrganInfo", null, new Object[] {
organInfoVO.getBeginDate(),
organInfoVO.getEndDate(),
organInfoVO.getOrganNo(),
organInfoVO.getOrganName(),
organInfoVO.getIsActive() },
pageModel);
}
在我们BaseDAOSupport中提供findPageByHql以及其它大量针对数据库查询的方法,在方法调用时只需要将参数完整化的以对象数组(或List),对于如状态选择:-1表示全部等特性,以及blank值的处理,我们提供了convertSqlParams(objects),convertSqlParams(obj,new Object[-1,""]),判断对象为空或-1则转为
null,用法:
return this.findPageByhql("hr_searchOrganInfo", null, new Object[] {
organInfoVO.getBeginDate(),
organInfoVO.getEndDate(),
organInfoVO.getOrganNo(),
organInfoVO.getOrganName(),
convertSqlParams(organInfoVO.getIsActive() ,-1)},
pageModel);
}
当然也可以批量转(转""为null)
return this.findPageByhql("hr_searchOrganInfo", null, convertSqlParams(new Object[] {
organInfoVO.getBeginDate(),
organInfoVO.getEndDate(),
organInfoVO.getOrganNo(),
organInfoVO.getOrganName(),
organInfoVO.getIsActive() }),
pageModel);
}
实现原理很简单(个人觉得这只是一个idea不属于技术范畴,说穿了谁都能实现只是实现的代码简繁而已),判断#[]中的问号所对应的参数是否为null(当然考虑like 和is,like将值直接替换进去,is 判断数据类型是否为null以及boolea类型,不是则将#[]内容切除),为null将#[]内容切除,是则将#[和]标记用""替换掉,这样整体
代码实现起来优雅简单,sql语句的可读性和可维护性大大增强!
当然你会找出一个极其变态难处理的sql整合(目前我们项目中还没有遇到),我们这个东西怎么做呢?其实
findPageByhql("hr_searchOrganInfo",param..param),第一个参数既可是是NamedQuery也可以是sql(hql)语句,自己组合咯,特殊问题特殊对待。
sagacity的宗旨在于不强迫你改变你的习惯,考虑特殊情况,为开发提供方便!
sagacity代码这几天整理一下,重新划分一下,将标签部分独立出来,减少耦合性将尽快对大家提供后台开发所需要的代码!
期待第二讲吧!
谢谢支持!
另外对于参数位置不容易记问题,其实我们这边是提供了
/**
* @todo 分页查询
* @param hql
* @param paramNames
* :参数名称
* @param params
* :参数值
* @param paginationModel
* :分页模型,含页号
* @return
*/
protected PaginationModel findPageByhql(final String hqlOrNamedQuery,
final String[] paramNamed, final Object[] paramsValue,
final PaginationModel paginationModel) {
}
paramNamed 参数方式的。
哈哈,说一下ibatis,ibatis确实能够实现类似的功能,但个人觉得没有这个灵活和优雅,下面发一张图,ibatis多条件动态查询无法让sql看起来如此优雅整洁:
再对比一下ibatis的做法:
<statement id="someName" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="where">
<isGreaterThan prepend="and" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
<isNotNull prepend=”and" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
</dynamic>
order by ACC_LAST_NAME
</statement>
效果一目了然了,代码多,还破坏了sql的整体性