该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-28
原来一个项目的查询也是用HQL写,但是后来发现生成的语句效率过低且无法优化,特别是继承层次比较深的对象,后来改成不用HQL,Hibernate只做CRUD,用SQL做查询,出二维表形式,不经过OO层,因为查询界面上一般也是二维表的形式。
这样有另外一个问题是有时候会有查询出来的东西需要是Object的,比如某个地方有一个Object缓存,所以还是用了部分HQL语句,后来Hibernate对Native SQL支持得比较不错了,想想,可能用Hibernate+Native SQL应付这个问题比较好。 准备下一个项目就用Hibernate做CRUD,SQL做查询,需要的地方用Hibernate+Native SQL,这样,SQL想怎么优化就怎么优化。 |
|
返回顶楼 | |
发表时间:2007-03-28
那你可以考虑用ibatis。
|
|
返回顶楼 | |
发表时间:2007-03-28
考虑过,考虑到不愿意写标准的CRUD的SQL和一对多,多对一的SQL这种劳工活,最后考虑还是用Hibernate来实现CRUD及关联获取。BTW,如果谁知道有自动生成一对多、多对一关联获取对象的代码生成器,请告知一下。
|
|
返回顶楼 | |
发表时间:2007-03-28
Readonly 写道 downpour 写道 这个问题又要牵涉到一个哲学问题上来了,估计完全取决于个人的喜好和接受程度,不做讨论也罢。我读了老马的文章,感觉就是他依然无法说服我。呵呵,可能是我的固有思想还没有转过来。
也可能取决于个人的经历吧,如果一个人以前项目经验是经常写sql procedure,他肯定认为挂3个条件的简单到不能再简单的where语句就可以搞定的事情,而你却要用遍历集合,然后依次判断,简直是自虐... 至于说遍历代码看起来不清楚,那只是语法的问题,比方说这样代码和sql where其实已经没有什么区别了 def not_completed_tasks_before(date) tasks.find_all(|t| t.assigned_date > date && t.status != 'completed' && t.status != 'pending') end 绝对不仅仅是语法的问题。看起来简单的一句SQL,在执行时oracle会在背后做很多很多事情,根据你要查询的各表的数据量,字段是否有索引,索引的类型等等,来生成一个最佳的执行计划,从而提高查询的效率。在java或ruby里面做筛选就失去了这个特性。这也是做复杂系统时不得不考虑的一个因素,有时候你没有选择,只能拼出where语句让数据库去做。 |
|
返回顶楼 | |
发表时间:2007-03-28
seacat 写道 Readonly 写道 downpour 写道 这个问题又要牵涉到一个哲学问题上来了,估计完全取决于个人的喜好和接受程度,不做讨论也罢。我读了老马的文章,感觉就是他依然无法说服我。呵呵,可能是我的固有思想还没有转过来。
也可能取决于个人的经历吧,如果一个人以前项目经验是经常写sql procedure,他肯定认为挂3个条件的简单到不能再简单的where语句就可以搞定的事情,而你却要用遍历集合,然后依次判断,简直是自虐... 至于说遍历代码看起来不清楚,那只是语法的问题,比方说这样代码和sql where其实已经没有什么区别了 def not_completed_tasks_before(date) tasks.find_all(|t| t.assigned_date > date && t.status != 'completed' && t.status != 'pending') end 绝对不仅仅是语法的问题。看起来简单的一句SQL,在执行时oracle会在背后做很多很多事情,根据你要查询的各表的数据量,字段是否有索引,索引的类型等等,来生成一个最佳的执行计划,从而提高查询的效率。在java或ruby里面做筛选就失去了这个特性。这也是做复杂系统时不得不考虑的一个因素,有时候你没有选择,只能拼出where语句让数据库去做。 只要对象数据已经在内存里了, 遍历的速度是很快的. 下面是遍历n层满二叉树的时间评测结果, 可以看到全遍历12层的一个树, 约有8000个节点时都用不到 1ms. 这时候甚至等不到你Oracle开始计算优化策略, 通过socket传输SQL命令的当儿, 这边已经遍历完了. 另外其实SQL优化对复杂查询很大帮助, 但对简单查询来说反而增加分析开销. H2DB 自己评测出的 OPS 比HSQL高很多, 但是跑PolePosition评测反而比HSQL慢, 作者就说这是因为H2DB的优化器下的功夫更大. SQL优化也并不总是能更有效, 因为数据库去执行SQL的时候, 要应付的是整张表的数据, SQL的天性就是得根据条件过滤; 而内存对象集合却是已经过滤好的部分对象了. 如果这个比例超过一定尺度, 想象一下一张任务表里存的是全系统人员的数十万条任务条目, 而一个worker自己的task列表只有20条任务时. 服务器端解析SQL需要的时间可能都比直接遍历20个对象要长, 还要加上后面选择优化方案, 制定执行计划, 再实际的执行, 最后通过socket返回结果的时间. 就算遍历时没有更多索引辅助, 也没有什么可优化的地方, 只要数据量够少, 比起来SQL来还是会更快. |
|
返回顶楼 | |
发表时间:2007-03-28
downpour 写道 所以说到底,还是要具体情况具体分析。动态语言在语法上的天然支持使得很多原本在Java中很复杂的事情变得异常简单,这也是Domain Logic可以进一步扩展其领地的一个重要前提。
我在1年多以前经历了一个新西兰专家设计的项目,整个项目的持久层由Hibernate实现,不过专家认为SQL或者HQL就是对OO的一种亵渎,所以强迫我们将所有的查询都封装在Domain Model中,通过遍历集合过滤来完成查询。 可能是当年这个项目的惨痛回忆,让我始终无法接受集合遍历这个东西吧。不过我不得不说,如果有类似Ruby这样的语法级别的支持,我相信我愿意重新尝试一下这种方式,继续用Java的话,还是算了。 呵呵, These people are just sharing the prejudices of their "OO religion" with you. |
|
返回顶楼 | |
发表时间:2007-03-29
歆渊 写道 seacat 写道 Readonly 写道 downpour 写道 这个问题又要牵涉到一个哲学问题上来了,估计完全取决于个人的喜好和接受程度,不做讨论也罢。我读了老马的文章,感觉就是他依然无法说服我。呵呵,可能是我的固有思想还没有转过来。
也可能取决于个人的经历吧,如果一个人以前项目经验是经常写sql procedure,他肯定认为挂3个条件的简单到不能再简单的where语句就可以搞定的事情,而你却要用遍历集合,然后依次判断,简直是自虐... 至于说遍历代码看起来不清楚,那只是语法的问题,比方说这样代码和sql where其实已经没有什么区别了 def not_completed_tasks_before(date) tasks.find_all(|t| t.assigned_date > date && t.status != 'completed' && t.status != 'pending') end 绝对不仅仅是语法的问题。看起来简单的一句SQL,在执行时oracle会在背后做很多很多事情,根据你要查询的各表的数据量,字段是否有索引,索引的类型等等,来生成一个最佳的执行计划,从而提高查询的效率。在java或ruby里面做筛选就失去了这个特性。这也是做复杂系统时不得不考虑的一个因素,有时候你没有选择,只能拼出where语句让数据库去做。 只要对象数据已经在内存里了, 遍历的速度是很快的. 下面是遍历n层满二叉树的时间评测结果, 可以看到全遍历12层的一个树, 约有8000个节点时都用不到 1ms. 这时候甚至等不到你Oracle开始计算优化策略, 通过socket传输SQL命令的当儿, 这边已经遍历完了. 另外其实SQL优化对复杂查询很大帮助, 但对简单查询来说反而增加分析开销. H2DB 自己评测出的 OPS 比HSQL高很多, 但是跑PolePosition评测反而比HSQL慢, 作者就说这是因为H2DB的优化器下的功夫更大. SQL优化也并不总是能更有效, 因为数据库去执行SQL的时候, 要应付的是整张表的数据, SQL的天性就是得根据条件过滤; 而内存对象集合却是已经过滤好的部分对象了. 如果这个比例超过一定尺度, 想象一下一张任务表里存的是全系统人员的数十万条任务条目, 而一个worker自己的task列表只有20条任务时. 服务器端解析SQL需要的时间可能都比直接遍历20个对象要长, 还要加上后面选择优化方案, 制定执行计划, 再实际的执行, 最后通过socket返回结果的时间. 就算遍历时没有更多索引辅助, 也没有什么可优化的地方, 只要数据量够少, 比起来SQL来还是会更快. 你说的还是少量数据,能够全部加载到内存的情况,如果是要从1000万条数据里面取100条,那遍历的开销恐怕也不能忽略不计了。数据库有索引机制,能高效地完成这样的任务,如果没有任何索引,即使能将1000万条记录全部装入内存,处理起来也是难以接受的。 |
|
返回顶楼 | |
发表时间:2007-03-29
seacat 写道 你说的还是少量数据,能够全部加载到内存的情况,如果是要从1000万条数据里面取100条,那遍历的开销恐怕也不能忽略不计了。数据库有索引机制,能高效地完成这样的任务,如果没有任何索引,即使能将1000万条记录全部装入内存,处理起来也是难以接受的。 加载到内存里的对象图, 通常都是按对象相关性组织的, 这样才是Domain Model. 它应该直接保存着指向那100条数据的集合, 用到的时候是直接遍历这100条数据, 而不是全部的1000万记录. 有谁会设计一个Domain Model必须把一张表的全部数据, 不管相关不相关的, 都先加载到内存再说的吗. SQL查询的时候才需要面对一张表的全部1000万条记录; 如果通过Domain Model处理, 也需要从一张表里的全部记录筛选自己相关的部分, 那么首先这不是一个像话的Domain Model, 其次如果不得不做这种事儿, 确实还不如用SQL. |
|
返回顶楼 | |
发表时间:2007-03-29
要看项目类型。
对我来说,把SQL返回的结果集转换为java对象的唯一驱动力就是在于缓存。 就如同庖丁解牛,当你做某一种业务超过2年,遇到所有可能的痛苦,特别是性能问题,你自然对整个业务的全部细节洞悉,此时,你不得不去进行优化,此时就知道缓存的重要性。 比如说,如果某种业务处理的数据库表总条数多,但频繁使用的对象有限,例如说结算的时候,大量使用一个月之内的数据,那么在没有ORM的时代,我会为这些对象建立缓存,以避免数据库的链接开销。 Hibernate为我做好了缓存,我要做的只不过是设置一下使用它的缓存,比我自己写要好多了,我当然用。 超越具体的业务,不好说某种方法一定超越另外一种方法。我从来不会追求完美的类封装。 |
|
返回顶楼 | |
发表时间:2007-04-01
其实做web应用应该很开心了。
如果涉及到remoting或者webservice,那种包含持久化能力(不论是显式编程的还是通过配置方式动态增强)的所谓rich domain model就是一个梦魇。 从spring的角度来看,一个组件是不是暴露为webservice或者其它别的远程访问协议,基本上可以通过配置做到,而组件代码本身是不预知的。但是,一旦用到了rich domain object,特别是动态增强型的rdo,那么为了顺利地远程访问,dto就变成不可缺少的了,因为在client端没有办法动态增强出rdo这样的一个类来。而一旦用到dto,各个环节上的重复性劳动马上大量增加。 就这个方面来说,.net下的dataset在java的世界内还没有匹配物,虽然怎么看怎么别扭,但通过remoting/webservice愣是能把整个填充过的各个表及其关系给方便而又彻底的序列化过去。实现离线操作和续弦。与之相比,hibernate的detached obj只是一个只能用于进程内传递的实现,根本无法跨越边界。 所以现在特烦那种带有持久化逻辑的rich domain obj. |
|
返回顶楼 | |