klingon

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

SQL、HQL、JDOQL、JPQL、Hibernate、iBATIS、ORBroker、DataNucleus、Spring Data这些东西究竟要给我们做什么,好多人当然也包括我不懂,可能作者这个2005年的博客会给大家说明一些,上链接:

http://jishudaima.iteye.com/blog/954939

无论用什么ORM方案,查询语句总还要写的,"如何让它容易写些"怎么也不outdate.
什么样的SQL最好看好写呢? 我觉得一段有着良好分行与缩进,中间没有太多+号或者java代码这类杂质的就已经是很好的了。

可实际情况是,当查询复杂时,上面的要求很少达到。

1.最倒霉的程序员会看到什么呢?他会看到一堆拼接SQL的API, 如Hibernate的Critertal
或者Team里的天才主力为了对付那些烦人的分号和"And "而写的SQL Builder类。
但是,对于人这种高智慧生物来讲,理解一段DSL语言要比读懂一组API容易得多(当然,对于机器来说刚好调转),这也就是为什么当初全世界一起设计的是一组SQL语言标准,而不是一套查询API了。
所以,我觉得API式的方案只在非常机械,同时复杂度又不高的动态环境里才适合使用。


即使没那么倒霉,有些麻烦还是逃不掉......

2. SQL经过复杂拼接,读程序很难再把握其全貌与来龙去脉
有时是为了根据用户的输入与选择而动态输入SQL。
更多时候是为了SQL的重用(鉴于SQL代码比java代码还要难读难改,而且没有refactor工具,SQL达到最大重用,尽量减少duplicate是对日后维护人员的最大恩赐),少不了非常多的SQL拼接。

有些拼接是几个String变量眼花聊乱的相加,有些还封装到不同函数里。
这时程序员只有在最后一刻把组装好的sql打印出来并把它重新格式化一遍,才可能是了解它的意义,否则单靠那些字符串变量和函数上的似通非通的注释,除了原作者,其他人好难明白它的意思。

另外还有两样麻烦事情逃不掉就是:

3.如果用Prepared statement,?号一多,想对号入座就很困难
如果有什么修改就更会引发雪崩般的恐怖。如果不用prepared satement,直接在把值写在SQL里,一方面性能上可能对不起用户,另一方面N多+号和双引号也让写的读的人很不爽。

4.还有就是SQL的对齐缩进换行
因为Java的String 不支持Mutli-line,连J2SE5.0也做不到这点,(Groovy倒是支持了),每换一行就要写上一些+号和双引号,如果SQL是一气呵成的倒没什么,如果是要反复修改时,不停维护这些+号和双引号就有点烦了。
(还有一些更无聊的人,在根本不是瓶颈的地方追求效率,用到stringBuffer.append(),和DB查询相比这根本实在微不足道啊)

所以,我们无论是用JDBC还是Hibernate,JDO,都可以在上面作一个处理查询语言的小框架(一千几百行就够了),多少消除一点上面提及的不便。

这篇讨论那个存在于普通SQL/HQL语句 与 JDBC/HIbernate之间的,一千几百行代码量的SQL处理层怎么写。

开源项目里,iBATIS这个以SQL为基础的ORM方案可以参考,另外还有它的新竞争对手ORBroker,后生可畏、后发制人,易用性方面走得更远。

1.针对SQL的换行和对齐,无非就是把它写到XML里面
   这样就可以不受Java String不能换行的鸟气了。
   这时的问题就是,饱含逻辑的SQL代码被从业务类里面分离出来了,我又不是很喜欢。
   还有一个未来的方案是用Groovy来写业务类,然后把它编译成Java类。可爱的Groovy支持
   String sql = "select *  from foo
                          where ....."

2.针对N多的?号参数,无非就是使用命名参数
    //把sql编译成PreparedStatement, 并对属性赋值
    String sql = "select * from dandy  where name=:name";
    query.setParam("name","gigix");

   做得好时,还应该支持内嵌的属性,解决属性同名的问题
   String sql = "select * from dandy,pet where dandy.name =:dandy.name and pet.name=:pet.name"
   query.setParam("dandy",gigix);
   query.setParam("pet",pet); 

    最后, 还应该支持对象里全部属性的自动绑定,简化书写
    //把gigix对象里的age,name属性自动绑定
    String sql = "update  dandy  set age=:age where name=:name "
    query.setObjectAsParams(gigix);

3. 解决拼接SQL(1)--优先使用Replace而非拼接SQL, 把SQL的概貌反映出来
    String sql = "select * from foo order by {{sortColumn}}";
     ....//条件判断语句
    query.setReplace("sortColumn","code");
 
    在整体的把握上, 要大大优于下面的迷宫阵法

    String sql="select * from foo";
    ...//条件判断
    sql+="order by code";

4.解决拼接SQL(2)--把可重用的SQL统一管理
    依然是整体把握优于局部拼接的思想
    对于可部分重用的SQL,现在的开发人员要不重用,完全Copy Paste,一旦要修改就改得山崩地裂
    要不就是把重用和不重用的部分复杂拼贴,又摆一个迷宫阵法出来。

    O/R Broker的做法值得参考:

<sql-statement id="getEmployees" result-object="Employee">
SELECT 
*
FROM 
Employee

<append-statement id-suffix="ById">
WHERE
EmployeeId = :id
</append-statement>

<append-statement id-suffix="BySalaryRange">
WHERE
Salary BETWEEN :lowSalary AND :highSalary
</append-statement>

</sql-statement>

然后你getEmployeesByID 和  getEmployeesBySalaryRange可以返回不同的SQL

5.拼接SQL(3)--Velocity/Freemarker永远都是文本生成的好工具

    Web页面生成也好,代码生成也好,Template Engine都被证明比pure java code好得多,否则大家怎么不个个去写Servelet呢,生成SQL当然也不例外。O/R Broker就有这个功能,聪明之处还在于,直接调用Velocity引擎就行了,不花自己一行代码。

<sql-statement id="getEmployee"> SELECT EmployeeId,EmployeeName,Salary, FROM Employee #if ($employee.id) WHERE EmployeeId = :employee.id #end </sql-statement>
posted on 2011-12-06 16:03  klingon  阅读(397)  评论(0)    收藏  举报