`
wyuch
  • 浏览: 74535 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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

 
阅读更多
  我思考了良久才决定发这篇文章,各位老大手下留情。

  假设有一张表Student,有几个字段ID,Name,Gender,BirthDay,实际上这么一个从数据库设计中直接生成的类就可以很好地满足我们对于ORM的要求:

public class StudentSchema extends Schema {
	private int ID;

	private String Name;

	private String Gender;

	private Date BirthDay;

	public static final TableName = "Student";

	public static final PrimaryKeys PKs = new PrimaryKeys(new String[] { "ID" });

	public Date getBirthDay() {
		return BirthDay;
	}

	public void setBirthDay(Date birthDay) {
		BirthDay = birthDay;
	}

	public String getGender() {
		return Gender;
	}

	public void setGender(String gender) {
		Gender = gender;
	}

	public int getID() {
		return ID;
	}

	public void setID(int id) {
		ID = id;
	}

	public String getName() {
		return Name;
	}

	public void setName(String name) {
		Name = name;
	}
}


  其中Schema是一个公用类,里面主要是提供了fill(),update(),delete(),backup()等功能。其中fill()就是通过主键返回一条记录,其他几个方法就不用说了吧。

  但反观Hibernate呢?我们需要一个与StudentSchema类似的POJO类,一个DAO类,一个*.hbm.xml映射文件,才能达到类似的效果,机制繁琐,性能消耗明显要高。

  我不知道为什么这么简单的高下立见的对比没有引起大家的注意,我也承认我对Hibernate浅尝辄止,并且我估计会引来大片的攻击,但我确实想像不出Hibernate有可能会比这更简洁更高效。在我们的实际应用中,StudentSchema是由工具从PowerDesigner中直接生成的,开发人员根本不管ORM,直接使用即可。StudentSchema是硬编码的类,不需要进行字节码动态生成,不需反射,不需配置文件,十分简单。

  更重要的是,我们这个ORM实现总共才几千行代码。Hibernate功能很多,但我们的ORM实现虽然代码很少,但己能覆盖实际开发中的全部ORM需求。我将以回复的方式加以说明。
分享到:
评论
131 楼 bigkai13 2009-06-12  
我觉的hibernate最大好处就是引入了XML文件来配置数据库。  如果一个程序用XML做为灵魂,那么这个程序将非常的强大。 好的东西成本比较高随之也就越值钱。
130 楼 yulenice 2009-06-12  
hibernate可以做到粒度很细,颇多优化,例如代理等.而且hibernate也是标准的参与者.有它基本就够了.正如楼主说的,学习成本较高.
当然允许多样化的存在

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

129 楼 ywlqi 2009-06-12  
我已经放弃Hibernate了,现在用spring JdbcDaoSupport ,感觉很爽
128 楼 icewubin 2009-06-11  
wyuch 写道
感谢楼上两位,看明白了,不需要变量传递对重构要有利一些。

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

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

但是这仅仅是一级缓存的适用场景之一而已,充分利用一级缓存的特性,还有其他很多用途,不在此一一举例子了,一级缓存的讨论到此为止吧。
127 楼 lqql 2009-06-11  
说实话Hibernate是个好东西,关键还是看使用的人.
但是常常这样的人比较少.
从我接触的别人使用Hibernate的情况....简单的增删改用Hibernate,复杂的查和批用JDBC....
这样的使用让我很无语....简单的用Hibernate,复杂的用JDBC,这实际上不是Hibernate的初衷啊....如果真是这样,Hibernate也没有存在的价值了.如果使用的状况能反过来,我想Hibernate才能发挥出它真正的威力吧
126 楼 wyuch 2009-06-11  
感谢楼上两位,看明白了,不需要变量传递对重构要有利一些。

我是不是可以理解成:一级缓存对性能方面其他没有什么优势,他的好处在于不需要配置,少写几行代码,而且较为优雅?
125 楼 liangx 2009-06-11  
LZ的东西其实就是一个JDBC的封装.我公司新来了个同事也自己搞了个叫SDK的封装.把JDBC封装起来就和Hibernate比较了.想不到这里都碰到一个.不过还是感谢LZ.能够从给LZ的板砖文章中看很多对Hibernate的一些基本理论.受益匪浅.
124 楼 icewubin 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};
}
123 楼 daquan198163 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一级缓存不正好帮我们做了这个繁琐的工作么,而且是以很优雅的、透明的方式。
122 楼 wyuch 2009-06-11  
xiaozhen57520 写道
楼主的cms做的非常不错,很漂亮。不过这个框架不能算orm框架,没有表关系的管理,只能算是om.


感谢夸奖。

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

像前面有朋友回复说的一样,实际上不是我这个东西不是ORM,而是Hibernate已经远超出一个ORM的范围,提供了一个全方位的持久化解决方案。
121 楼 wyuch 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请求,怎么可能命中一级缓存?
120 楼 zypro 2009-06-11  
wyuch 写道

而且我希望将缓存控制在自己手里,不要自动给我缓存了,有可能这是在浪费内存。


当lz介绍完他那简洁,高效,无所不能的orm之后,应该就要介绍他自己写的缓存了.

字里行间我看出他的缓存有以下几个优点:

简洁---无需配置,memcache太难用了,要反思
高效---采用了天下第一的LRU算法,效率一定比其他缓存高. 其他第三方缓存一定慢,又是策略,又是分布式,毫无疑问.
无所不能---数据库? entity? 有什么是它不能缓存的吗? 都可以... 而且缓存可以控制在自己手中,想怎么用都可以,不会浪费内存.

当然,在如此优秀的产品面前,Hibernate的缓存算老几? 确实要反思一下,这么多年都干嘛去了,连个缓存也做不好. 人家都说了,是浪费内存.
119 楼 mikeandmore 2009-06-11  
个人觉得也就是算个jdbc的wrapper。。。
118 楼 xiaozhen57520 2009-06-11  
楼主的cms做的非常不错,很漂亮。不过这个框架不能算orm框架,没有表关系的管理,只能算是om.
117 楼 icewubin 2009-06-10  
关于引用占用内存很少的说法,建议你自己做个很简单的实验,新建100000个String对象,然后新建10-100个LinkedList,每个List都添加这100000个对象的引用,看看内存增加了多少。我认为一个引用(指针)占用的内存,相对于这个引用指向的对象所占用的内存来说,少2-3个数量级以上。

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

下面是复用的例子,假设在一个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)};
}

116 楼 wyuch 2009-06-10  
royzhou1985 写道
楼主什么时候发源码出来啊
记得发一份给我哦
royzhou1985@foxmail.com


好,会公布SVN地址,和一个OA项目一起发布
115 楼 royzhou1985 2009-06-10  
楼主什么时候发源码出来啊
记得发一份给我哦
royzhou1985@foxmail.com
114 楼 icewubin 2009-06-10  
wyuch 写道
icewubin 写道
不想和你说了,我虽然做了这么多假设,但是都是会碰到的,这些假设更多的是为了简化例子的复杂性,我不可能完整的把真实的例子原封不动的一个字一个字写上来给你上课。

你现在给人感觉,我写的这些例子你连深入思考都不愿意,我讲的也没意思,就当我什么都没说好了。


你那14亿条记录、不能建索引和外键的假设,还不够极端,还都是会碰到的呀
我都说了LRU,你还在说“你一启动就把所有14亿用户信息扔到缓存中”,反倒是你来说我没有认真看你帖子云云......
你在说缓存的例子,我也在分析你缓存的例子,非得我认同你的观点才算是深入思考?

“14亿用户信息扔到缓存中”这句算我是说错了,我后来不是也说了,如果这个例子实际去做,我也会这么缓存的。

你还一定要较真,那我来列列你说错过的话:
wyuch 写道
但反观Hibernate呢?我们需要一个与StudentSchema类似的POJO类,一个DAO类,一个*.hbm.xml映射文件,才能达到类似的效果,机制繁琐,性能消耗明显要高。

wyuch 写道
我们的ORM实现虽然代码很少,但己能覆盖实际开发中的全部ORM需求。

wyuch 写道
这种模式在简洁性上面有着巨大的优势

wyuch 写道
我的ORM框架要写的代码比Hibernate少,……,我想这应该是没有太多疑问的。

wyuch 写道
Hibernate功能强,但又有配制又有反射又有动态字节码,所以效率比我的差,我想这也是没有疑问的。

wyuch 写道
第二是复杂框架可能会带来潜在的性能问题,我想一个成熟的框架本身不会直接有问题,不然也生存不下来,但使用它有可能带来性能问题,并且出现性能问题时追踪不易。





113 楼 wyuch 2009-06-10  
icewubin 写道
不想和你说了,我虽然做了这么多假设,但是都是会碰到的,这些假设更多的是为了简化例子的复杂性,我不可能完整的把真实的例子原封不动的一个字一个字写上来给你上课。

你现在给人感觉,我写的这些例子你连深入思考都不愿意,我讲的也没意思,就当我什么都没说好了。


你那14亿条记录、不能建索引和外键的假设,还不够极端,还都是会碰到的呀
我都说了LRU,你还在说“你一启动就把所有14亿用户信息扔到缓存中”,反倒是你来说我没有认真看你帖子云云......
你在说缓存的例子,我也在分析你缓存的例子,非得我认同你的观点才算是深入思考?
112 楼 icewubin 2009-06-10  
不想和你说了,我虽然做了这么多假设,但是都是会碰到的,这些假设更多的是为了简化例子的复杂性,我不可能完整的把真实的例子原封不动的一个字一个字写上来给你上课。

你现在给人感觉,我写的这些例子你连深入思考都不愿意,我讲的也没意思,就当我什么都没说好了。

相关推荐

Global site tag (gtag.js) - Google Analytics