锁定老帖子 主题:动态SQL的实现方式
该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-14
最后修改:2010-04-15
在做企业级应用的时候会有很多的系统配置和 SQL语句需要编写。按照平常的做法是写在代码中,以接口或者 final static String的方式来定义变量,每次修改都要改动代码。好一点的做法是写到 properties或者有结构的 XML文档中。但是也是不能动态修改的,这里给大家介绍一种动态装载配置的做法。对于那种很多的查询条件的情况下尤其适用,只要给对应的参数就可以构造出对应的 SQL,采用 Hibernate的 QBC也可以达到该效果,二者配合使用相得益彰。 主类: ConfigCode。 /** * 获取相应的配置信息 * @param signature 对象名 * @return String */ public String getCode (String signature); /** * 获取相应的动态配置信息 * @param signature 对象名 * @param map 对象方法 * @return String 对象 */ public String getCode(String signature,Map parameterMap ); /** * 利用配置文件来进行初始化工作 * @param fileName 配置文件名字 * @return 初始化是否成功 */ private boolean init(String fileName);
基本思路: 将配置放到特定格式的 XML 文档中,在系统启动的时候加载进入内存。在需要修改的时候更改配置文件,不用重启服务器直接生效。其工作方式有两种,一种是 product 模式,系统不会检测文件的变更时间,如果有改动的话需要手动更新,这样提高了性能。还有 debug 模式,系统实时监控文档的变化,一旦文档发生变化即时重载该文件内容。使用前先实例化该类。 ConfigCode sQlCode = ConfigCode.getInstance ( "classpath*:DAL.cfg.xml" ); sQlCode.setDebug( true ); // 设置 debug 模式
XML 文档有个总配置文档 DAL.cfg.xml 来控制配置文档放在哪里。其格式如下: <? xml version = "1.0" encoding = "UTF-8" ?> < DataAccessLayer > /** * <p> Code 主配置文件解析类 . </p> 1 、每一行只有一对大括号 {} 2 、在 { 之后紧跟!表示这个条件即使没有传值过来就采用默认值 "", 如果带有 || 则将 || 之前的作为默认值 3 、如果 { 之后不跟 !, 表示如果不传值的话这个条件忽略 4 、用 $$ 括起来的参数( key )将会用 parameterMap 中的 value 代替 5 、采用 objectName.MethodName 作为 key 放在 parameterMap 中 * @author Newway Jan 26, 2008 Email:gmhwq@126.com */ < MappingFiles > < Mapping resource = "classpath*:sql/**/*.dal.xml" /> </ MappingFiles > </ DataAccessLayer >
支持通配符,意思是匹配 classpath 下 sql 目录里面所有以 dal.xml 结尾的文件。 如果有多个配置文件可以写到总配置文档 DAL.cfg.xml 中。
好了,看看配置文件是怎么写的。配置是分为两层的,第一个前缀是指定某些功能模块,后面的是配置的名称,以防止名称的冲突。系统在启动过程中如果发现有冲突或在控制台做做出 WARNING 。 config.dal.xml <? xml version = "1.0" encoding = "UTF-8" ?> <!-- edited by Hewq (Bingo) --> < DataAccessLayer > < BusinessObjects > < Object objectName = "config" > < Method name = "isDebug" > 1 </ Method > < Method name = "pageSize" > 10 </ Method > < Method name = "batchSize" > 1000 </ Method > < Method name = "MaxIndexJpg" > 7 </ Method > < Method name = "MaxFileSize" > 3145728 </ Method > <!-- contextPath ,if true path equals "/" --> < Method name = "ROOT" > false </ Method > < Method name = "DEFAULT_SHOP" > legenddesign </ Method > < Method name = "DOMAIN_NAME" > http://www.legendesign.net </ Method > </ Object > </ BusinessObjects > </ DataAccessLayer >
以上是配置项,还算简单吧。看看下面的动态 SQL :
< Method name = "getPaihang" ><![CDATA[ select hw from Hw hw ,Sort sort where hw.sortId = sort.sortId and sort.userName = ? order by hw.hwBuys desc ]]></ Method > < Method name = "getShopDetail" ><![CDATA[ select new ShopDetail(s.userId,s.web,s.sitename,s.maddr,s.msn,s.mname,s.code,s.ymaddr, s.ymname,s.storeName,s.visitTimes,s.modifyTime, u.userMail,u.userTel,u.userPostcode,s.colorStyle,s.briefDesc) from ShopDetail s,UserDetail u where s.userId =u.userId and s.status = 1 and s.storeName = ? ]]></ Method > <!-- 登录历史统计 --> < Method name = "loginHistorySum" > <![CDATA[ select user_Name,count(*) from t_Login_History where 1=1 { and user_name = '$userName$'} {? and time >= $startTime$} {? and time <= $endTime$} group by user_Name order by count(*) desc ]]> </ Method >
此处只是列出其中两个例子做说明,其余的看附件。有了这个动态的 SQL 之后就不用再代码中写那些烦人的 if else 的嵌套了。传递一个 map 进来自动匹配对对应的 SQL 出来,其原理是采用正则表达式来做的,具体业务不同的话可以改造实现。 例如 loginHistorySum ,如果传进来的 Map 为空则得出的 SQL 为: select user_Name,count(*) from t_Login_History where 1=1 如果 Map 包含 userName ,则 select user_Name,count(*) from t_Login_History where 1=1 and user_name = 'userName'
//'userName' 将会用实际值代替,如果前面有问号 ? 的则用问号 ? 代替。 还有一条规则:之后紧跟!表示这个条件即使没有传值过来就采用默认值 "", 如果带有 || 则将 || 之前的作为默认值。
看看 Client 调用的例子: String sql = ConfigCode .getInstance ().getCode( "biz.loginHistorySum" , map); String countSql = ConfigCode .getInstance ().getCode( "biz.loginHistoryCount" , map);
如果你修改了 XML 文件不知道是否做了更改,也可以通过界面来观看改值的变化,也可以通过界面来刷新 XML 配置的缓存。
看看上面的配置项,是从 http://www.legendesign.net/system/sql/sqlCode.jsp 拷贝而来的。但是由于改页面需要系统权限才能看到。这些还算是比较机密的信息吧,权限又是另外一个大话题了。如果有什么疑问可以加入我们的 QQ 群 96642931 讨论, gmhwq@126.com Newway 。代码见附件。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-04-15
看起来和我当初做的仓库猫有点像哦。
|
|
返回顶楼 | |
发表时间:2010-04-15
ibtias 就能全部做到.
特别是用了abator之后 |
|
返回顶楼 | |
发表时间:2010-04-15
lz能说明下和iBatis相比的优势么
之前看的 还木有ib好用 |
|
返回顶楼 | |
发表时间:2010-04-15
把程序中组装sql的过程放到了xml中集中管理,和ibatis很像
|
|
返回顶楼 | |
发表时间:2010-04-15
楼主为什么不用ibatis?
|
|
返回顶楼 | |
发表时间:2010-04-15
恩,XML集中进行管理,有效提高了程式的灵活性,学习中。。。 UP
|
|
返回顶楼 | |
发表时间:2010-04-15
山寨 ibatis?
|
|
返回顶楼 | |
发表时间:2010-04-15
山寨 ibatis? 不太像啊!
|
|
返回顶楼 | |
发表时间:2010-04-15
最后修改:2010-04-15
请参看,当然我们现在用的已经更加简单了
http://zhongxuchen.iteye.com/admin/blogs/334013 用法一: <!-- 配置辅助sql处理工具用于sql查询条件的处理 --> <bean id="sqlToyContext" name="sqlToyContext" class="org.sagacity.sqltoy.SqlToyContext"> <property name="sqlcfgPlugin"> <bean id="sqlcfgPlugin" name="sqlcfgPlugin" class="org.sagacity.sqltoy.plugin.impl.SqlConfigBaseXML"> <property name="cache"> <bean class="org.sagacity.sqltoy.cache.impl.HashSqlCache" /> </property> <property name="debug" value="true" /> <property name="dialect" value="oracle" /> <property name="enableInStrategy" value="false" /> <property name="resourcesDir" value="classpath:/com/ccb/" /> </bean> </property> </bean> 用法二: <!-- 配置辅助sql处理工具用于sql查询条件的处理,基于hibernate hbm.xml配置文件 --> <bean id="sqlToyContext" name="sqlToyContext" class="org.sagacity.sqltoy.SqlToyContext"> <property name="sqlcfgPlugin"> <bean id="sqlcfgPlugin" name="sqlcfgPlugin" class="org.sagacity.sqltoy.plugin.impl.SqlConfigBaseHibernate" /> </property> </bean> |
|
返回顶楼 | |