论坛首页 Java企业应用论坛

反思Hibernate,可以有更简洁、更高效的ORM实现

浏览 34576 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2009-06-11  
icewubin兄弟,你不要气愤我没看到你的东西,实际上我看了的,我也回答了,我再回答一遍你的问题。我们再各自仔细思考一下如何,反正这个帖就快被隐藏了。

引用

写个例子给楼主看看吧,写之前再强调一下,这句话楼主不要漏看了,14亿的那个例子是为了说明一级缓存基本工作原理的例子,一级缓存的价值在于只多占极少内存的情况下默认就提供一个事务中的缓存机制,不需要任何配置,不需要任何多余的编码,不需要自己设计缓存机制和边界,不需手动在事务结束时清除缓存,这个缓存的粒度规模和一般的LRU的业务缓存不是一个级别的,也不是一个层面上的。

我理解你的例子,Hibernate提供了一种自由,可以在一个事务里向持久层重复提交多次一模一样的数据查询请求,但只有一次会真正向数据库请求数据,其他的会一级缓存中直接读取。
我前面回复过你了的,我再详细解说一遍:
一、这种机会多不多我不知道,至少我很少遇到在一个事务里向数据库请求两次一模一样的SQL语句的情况。同时你举的例子假设条件太多了太极端,既然你经常遇到,请你举一个平时就能遇到的例子。所以你也不要因为这个生气。
二、我们仔细想想,为什么在没有类似一级缓存之类的东西甚至还没有Hibernate之前,我们没有干两次去取同样的数据的事情?其实很简单,我们取出来以后放在一个对象里,只需要在代码中保持这个对象的引用就行了。就像20个SQL,8个用户的例子,我们通常会在每次执行SQL之前检查是不是已经有该用户已经取出过了,几句很简单的代码,结果还是一样,最终只执行了8个SQL。Hibernate的优势就在于省了这几行代码,但他还是有坏处的,那就是:
三、为什么一些数据量特别大的情况下JDBC能够处理,而Hibernate会内存溢出,这中间没有内存浪费吗?这种浪费和一级缓存没有关系?

icewubin 写道
下面是复用的例子,假设在一个Service中,Spring配置了事务边界在Service的方法上:
//一个页面ajax的需求,显示某个用户的等级,等级根据用户的在线时间和一定的规则计算出来的
public String findUserGrade(String userId) {
    User user = userDao.findById(userId);
    long onlineTime = user.getOnlineTime();
    String gradeInfo = 。。。;//根据一些较复杂的算法,由onlineTime算出等级
    return gradeInfo;
}

//哪天另一个页面增加一个需求了,除了显示等级,还需要显示用户的发贴数,也是通过Ajax传递userId取数据
public String[] findUserBrief(String userId) {
    User user = userDao.findById(userId);
    int topicNum = user.getTopicNum();
    //因为一级缓存的存在,此时根本不需要重构来复用显示等级的算法,直接调用findUserGrade方法即可
    return new String[] {"" + topicNum, findUserGrade(userId)};
}


这个例子,我倒真是不明白,两个不同的页面,两次不同的Ajax请求,怎么可能命中一级缓存?
0 请登录后投票
   发表时间:2009-06-11  
xiaozhen57520 写道
楼主的cms做的非常不错,很漂亮。不过这个框架不能算orm框架,没有表关系的管理,只能算是om.


感谢夸奖。

我理解R是指关系型数据库吧,不是指表关系吧?我觉得我这个东西ORM还算是吧。

像前面有朋友回复说的一样,实际上不是我这个东西不是ORM,而是Hibernate已经远超出一个ORM的范围,提供了一个全方位的持久化解决方案。
0 请登录后投票
   发表时间:2009-06-11   最后修改:2009-06-11
wyuch 写道

icewubin 写道
下面是复用的例子,假设在一个Service中,Spring配置了事务边界在Service的方法上:
//一个页面ajax的需求,显示某个用户的等级,等级根据用户的在线时间和一定的规则计算出来的
public String findUserGrade(String userId) {
    User user = userDao.findById(userId);
    long onlineTime = user.getOnlineTime();
    String gradeInfo = 。。。;//根据一些较复杂的算法,由onlineTime算出等级
    return gradeInfo;
}


//哪天另一个页面增加一个需求了,除了显示等级,还需要显示用户的发贴数,也是通过Ajax传递userId取数据
public String[] findUserBrief(String userId) {
    User user = userDao.findById(userId);
    int topicNum = user.getTopicNum();
    //因为一级缓存的存在,此时根本不需要重构来复用显示等级的算法,直接调用findUserGrade方法即可
    return new String[] {"" + topicNum, findUserGrade(userId)};
}

这个例子,我倒真是不明白,两个不同的页面,两次不同的Ajax请求,怎么可能命中一级缓存?

注意红字部分。

wyuch 写道
为什么在没有类似一级缓存之类的东西甚至还没有Hibernate之前,我们没有干两次去取同样的数据的事情?其实很简单,我们取出来以后放在一个对象里,只需要在代码中保持这个对象的引用就行了。就像20个SQL,8个用户的例子,我们通常会在每次执行SQL之前检查是不是已经有该用户已经取出过了,几句很简单的代码,结果还是一样,最终只执行了8个SQL。

hibernate一级缓存不正好帮我们做了这个繁琐的工作么,而且是以很优雅的、透明的方式。
0 请登录后投票
   发表时间:2009-06-11   最后修改:2009-06-11
wyuch 写道

一、这种机会多不多我不知道,至少我很少遇到在一个事务里向数据库请求两次一模一样的SQL语句的情况。同时你举的例子假设条件太多了太极端,既然你经常遇到,请你举一个平时就能遇到的例子。所以你也不要因为这个生气。

例子已经举了,可能你没有理解。
wyuch 写道

二、我们仔细想想,为什么在没有类似一级缓存之类的东西甚至还没有Hibernate之前,我们没有干两次去取同样的数据的事情?其实很简单,我们取出来以后放在一个对象里,只需要在代码中保持这个对象的引用就行了。就像20个SQL,8个用户的例子,我们通常会在每次执行SQL之前检查是不是已经有该用户已经取出过了,几句很简单的代码,结果还是一样,最终只执行了8个SQL。Hibernate的优势就在于省了这几行代码,但他还是有坏处的,那就是:

还是那句话,我的例子已经很清楚了。
wyuch 写道

三、为什么一些数据量特别大的情况下JDBC能够处理,而Hibernate会内存溢出,这中间没有内存浪费吗?这种浪费和一级缓存没有关系?

这种情况我也说了,软件使用不当所致,你可以举出具体的问题代码,我都以一个一个的跟你说使用上的问题在那里,空对空地说内存溢出毫无意义。例如,有些人就不懂得hibernate有投影功能。
wyuch 写道

icewubin 写道
下面是复用的例子,假设在一个Service中,Spring配置了事务边界在Service的方法上:
//一个页面ajax的需求,显示某个用户的等级,等级根据用户的在线时间和一定的规则计算出来的
public String findUserGrade(String userId) {
    User user = userDao.findById(userId);
    long onlineTime = user.getOnlineTime();
    String gradeInfo = 。。。;//根据一些较复杂的算法,由onlineTime算出等级
    return gradeInfo;
}

//哪天另一个页面增加一个需求了,除了显示等级,还需要显示用户的发贴数,也是通过Ajax传递userId取数据
public String[] findUserBrief(String userId) {
    User user = userDao.findById(userId);
    int topicNum = user.getTopicNum();
    //因为一级缓存的存在,此时根本不需要重构来复用显示等级的算法,直接调用findUserGrade方法即可
    return new String[] {"" + topicNum, findUserGrade(userId)};
}


这个例子,我倒真是不明白,两个不同的页面,两次不同的Ajax请求,怎么可能命中一级缓存?

不是两次Ajax,单独分析findUserBrief方法,此方法主要目的是想复用已有的代码和算法(findUserGrade方法),但是直接调用前后会直接和间接的调用“userDao.findById(userId)”两次,有了现成的一级缓存,这两次查询只会产生一次sql查询。

顺带回答你提到的“只需要在代码中保持这个对象的引用”的问题,这个例子不是不可以,我已经说了,如果没有一级缓存的支持,而又想复用算法,势必要重构(其实就是你说的“只需要在代码中保持这个对象的引用”),有了一级缓存,类似的重构都可以避免,为了让你有直观的认识,现在把重构的代码展示如下:
//一个页面ajax的需求,显示某个用户的等级,等级根据用户的在线时间和一定的规则计算出来的
public String findUserGrade(String userId) {
    User user = userDao.findById(userId);    
    String gradeInfo = getUserGrade(user);//采用重构的方法
    return gradeInfo;
}
//重构出的封装“显示等级算法”的方法,为了便于回答楼主问题,这里参数用了user的引用
private String getUserGrade(User user) {
    long onlineTime = user.getOnlineTime();
    String gradeInfo = 。。。;//根据一些较复杂的算法,由onlineTime算出等级
    return gradeInfo;
}
//哪天另一个页面增加一个需求了,除了显示等级,还需要显示用户的发贴数,也是通过Ajax传递userId取数据
public String[] findUserBrief(String userId) {
    User user = userDao.findById(userId);
    int topicNum = user.getTopicNum();
    String gradeInfo = getUserGrade(user);//采用重构的方法
    return new String[] {"" + topicNum, gradeInfo};
}
10 请登录后投票
   发表时间:2009-06-11  
LZ的东西其实就是一个JDBC的封装.我公司新来了个同事也自己搞了个叫SDK的封装.把JDBC封装起来就和Hibernate比较了.想不到这里都碰到一个.不过还是感谢LZ.能够从给LZ的板砖文章中看很多对Hibernate的一些基本理论.受益匪浅.
0 请登录后投票
   发表时间:2009-06-11  
感谢楼上两位,看明白了,不需要变量传递对重构要有利一些。

我是不是可以理解成:一级缓存对性能方面其他没有什么优势,他的好处在于不需要配置,少写几行代码,而且较为优雅?
0 请登录后投票
   发表时间:2009-06-11  
说实话Hibernate是个好东西,关键还是看使用的人.
但是常常这样的人比较少.
从我接触的别人使用Hibernate的情况....简单的增删改用Hibernate,复杂的查和批用JDBC....
这样的使用让我很无语....简单的用Hibernate,复杂的用JDBC,这实际上不是Hibernate的初衷啊....如果真是这样,Hibernate也没有存在的价值了.如果使用的状况能反过来,我想Hibernate才能发挥出它真正的威力吧
0 请登录后投票
   发表时间:2009-06-11   最后修改:2009-06-11
wyuch 写道
感谢楼上两位,看明白了,不需要变量传递对重构要有利一些。

我是不是可以理解成:一级缓存对性能方面其他没有什么优势,他的好处在于不需要配置,少写几行代码,而且较为优雅?

不完全是,我举的这个例子还是太简单,这个简单是指“即使没有一级缓存也可以很方便的重构”,实际可能出现的某场景:你拿到的已经是别人写好的一堆代码,然后让你加一个功能,此时如果你要仔细分析别人写的代码加以重构利用不是不可以,时间成本会很高,而且重构会有出新bug的可能,往往是不敢随便动原来的代码的,此时要想复用原来代码的部分业务逻辑,一般会有各种技巧,如果有一级缓存的话,就能方便不少。

但是这仅仅是一级缓存的适用场景之一而已,充分利用一级缓存的特性,还有其他很多用途,不在此一一举例子了,一级缓存的讨论到此为止吧。
0 请登录后投票
   发表时间:2009-06-12  
我已经放弃Hibernate了,现在用spring JdbcDaoSupport ,感觉很爽
0 请登录后投票
   发表时间:2009-06-12   最后修改:2009-06-12
hibernate可以做到粒度很细,颇多优化,例如代理等.而且hibernate也是标准的参与者.有它基本就够了.正如楼主说的,学习成本较高.
当然允许多样化的存在

在manning persistence with jpa有说到.orm分为几层,而Hibernate属于最高的一层.楼主的可能属于第二层.或者说是hibernate的子集.hibernate在您的基本提供了结构和行为的missmatch的解决方案.

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics