`
wyuch
  • 浏览: 73869 次
  • 性别: 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需求。我将以回复的方式加以说明。
分享到:
评论
111 楼 wyuch 2009-06-10  
引用

怎么可能没有机会?我刚才的例子难道都是白举的,你到底看了没有?我写了这么多假设你都没看是吧。

你那例子是不是也太极端了,正是因为假设那么多,所以对我没有说服力。我特意说了“可能以前都是将查询出的数据作为参数传递”云云。我理解你的意思,用我的写法我new一个UserSchmea,fill()一次以后再次fill()会重复去执行SQL,但用Hibernate可以随便多次findById()但只执行一次SQL,但这有用吗?在没有Hibernate之前为什么我们不会干这种傻事?那就是因为“以前都是将查询出的数据作为参数传递”,没有必要四处调用fill或者findById,现在Hibernate提供给我们的只是一种自由,但这种自由有必要吗?我们自己保持对象的引用就可以了,何必再缓存一次引用呢?

引用

我还特地说了这个不需要任何变成手段,不需要任何配置,还说了和业务级的缓存不是一回事。您都没看是吧。

我的回复里没有反驳你这一条呀,你不能强迫我一定要正面重复你的看法呀。

引用
我还说了,一个事务结束,缓存就没有了,不占用内存的,因为一个事务中,你该取的数据本来就是取出来的,缓存的都是引用而已,事务一结束,缓存失效,如果实体对象也不再使用马上就会被JVM回收,怎么会占用内存?

这个我没有看到你有说,而且这是一个动态的过程,假设内存中同时存在的事务平均有50个事务,那就平均有50份缓存相关的数据,虽然数据本身是已经取出来的,但维护这些缓存的引用不需要内存吗?

引用

唉,你如果不愿意仔细看别人写的东西,那就没必要讨论下去了,鸡同鸭讲啊。

不要这么激动呀
110 楼 wyuch 2009-06-10  
icewubin 写道
楼主既然如此听不进任何建议,答非所问,还是自娱自乐比较好。

就等着楼主的框架开源后闻名全世界吧。


我回复了你的话你认为没有回复,难道非得我认同你的话才算是回复,才算是看了你的帖子?
我无语......
109 楼 icewubin 2009-06-10  
楼主既然如此听不进任何建议,答非所问,还是自娱自乐比较好。

就等着楼主的框架开源后闻名全世界吧。
108 楼 icewubin 2009-06-10  
wyuch 写道
我不会缓存14亿数据,用了LRU算法的。这个Map元素的最大值不用很大,看情况一两千即可,来一个用户先从Map中查询,没有就去数据库查,并根据LRU算法移出一个。

在同一事务中重复执行两次同样的SQL的机会很少,可能以前都是将查询出的数据作为参数传递,没有注意到类似于多次findById这样的需求。实际上可以用最傻瓜的方法处理你所举的例子:我们将每个用户查询的结果简单的放在临时HashMap中,每次查询时先去检查这个Map,效果和你举的例子完全一样。

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

怎么可能没有机会?我刚才的例子难道都是白举的,你到底看了没有?我写了这么多假设你都没看是吧。

我还特地说了这个不需要任何变成手段,不需要任何配置,还说了和业务级的缓存不是一回事。您都没看是吧。

我还说了,一个事务结束,缓存就没有了,不占用内存的,因为一个事务中,你该取的数据本来就是取出来的,缓存的都是引用而已,事务一结束,缓存失效,如果实体对象也不再使用马上就会被JVM回收,怎么会占用内存?

唉,你如果不愿意仔细看别人写的东西,那就没必要讨论下去了,鸡同鸭讲啊。
107 楼 wyuch 2009-06-10  
icewubin 写道

你还是没有仔细看我说的前提,一级缓存,不是二级缓存。如果还是这个例子,我自己做的话也会和你一样做一个键值对的缓存,这类缓存对应到Hibernate是二级缓存,跨事务的。

但是我都强调了,不是这种缓存,而且说了,这只是个例子,因为不是所有数据都能缓存的,有些情况不算是键值对,有些情况是数据量非常大,一级缓存在事务结束的时候都会释放内存的,我这里特地说了数据量非常大,你一启动就把所有14亿用户信息扔到缓存中,是想把JVM撑爆是吧。

还有我再提醒一下,即使内存够,JDK的LinkedHashMap在数据量超过10万以后就会有性能隐患,是不能和真正的缓存框架相提并论的。

缓存再简单都需要配置,hibernate的一级缓存是不需要配置的,不是二级缓存。

这种情况不少见,至少我经常碰到,我表明的意图就是,hibernate的一级缓存使用简单,默认就有,不需要任何配置,使得开发简单,更容易的复用事务脚本方法(例如findById之类的方法就可以很放心的被其他方法复用而不用担心性能问题)。这种情况和专门针对业务做的业务级缓存的概念是不一样的,这种在一个事务中的缓存技术是很实用的。


我不会缓存14亿数据,用了LRU算法的。这个Map元素的最大值不用很大,看情况一两千即可,来一个用户先从Map中查询,没有就去数据库查,并根据LRU算法移出一个。

在同一事务中重复执行两次同样的SQL的机会很少,可能以前都是将查询出的数据作为参数传递,没有注意到类似于多次findById这样的需求。实际上可以用最傻瓜的方法处理你所举的例子:我们将每个用户查询的结果简单的放在临时HashMap中,每次查询时先去检查这个Map,效果和你举的例子完全一样。

而且我希望将缓存控制在自己手里,不要自动给我缓存了,有可能这是在浪费内存。
106 楼 TheMarine 2009-06-10  
恕我愚见!
字面大家都很清楚Hibernate=冬眠,那么意思很清楚,hinernate要解决的是对象持久的问题,怎么就单单变成orm映射了呢?当lz的框架也可以很好的解决整个持久方案的时候,再比比hibernate是不是简单.
另外一个问题,orm到底是不是应该正向的去映射呢?从对象去导出数据库?如果是逆向,那我觉得什么orm都是累赘.
105 楼 icewubin 2009-06-10  
wyuch 写道
icewubin 写道
我再说一个Hibernate一级缓存的例子,为什么不说二级缓存,因为有争议,我也不推荐。

一级缓存和二级缓存最大的区别就是一级缓存只存在与一个事务中,事务结束,一级缓存也释放,这个功能是非常有用的。当一个业务逻辑很复杂的时候,你可以很放心的在一个事务中调用findById,而不用担心反复读取数据库。可能有人说,一个项目中80%都是CRUD,没那么复杂,所以我现在举个例子就是一个不复杂的业务查询中,一级缓存的价值。

假设一个管理页面上需要显示当前论坛最近发的20个帖子的信息,或者类似的需求,每一个帖子都需要显示该帖发帖人的姓名,假设数据库中两张表,帖子表和用户表,帖子表中存放了用户表的外键。有人看到这会说,用户ID和用户信息可以做缓存,JVM放不下,可以用memcached,这个就免了吧,我只不过举个例子,不是每种情况都适合做全局大规模缓存的。

再假设用户表中数据量很大,全中国14亿人吧,一人一条数据。帖子表,也没有给用户表外键做真的外键和索引。

好如果用JDBC如何实现(其实itatis和hibernate生成sql都一样)?
1.帖子表和用户表关联查询,因为我上面假设14亿人,帖子表中也没有物理外键或索引,所以这个两个表关联查询还是免了吧。

2.先一条sql查询帖子表最后10条记录,再20条sql查询用户表。啊,被逼的N+1了,呵呵,搞了这么多假设故意的,但是这些假设的前提在很多项目中是真实存在的。

继续说,如果是hibernate的话,不用join方式(两表关联方式)抓取数据,如果那20个用户都不一样,则也是N+1=21条sql,但是如果最后20帖中,是集中在8个人发的帖子,此时hibernate会命中一级缓存,最终就是8+1=9条sql。


我理解你的意思,但即便在你的假设前提下,不用Hibernate也很简单,使用外挂的支持LRU缓存(实际上可以用一个支持LRU的Hashmap,稍微改造一下LinkedHashMap就可以)缓存用户数据,效果完全一样。

其实你说的情况只会在JVM启动不久之后才会发生,系统运行一段时间后这8个人的数据都很有可能已经进入查询缓存了,虽然sql可能会有9次,但实际往数据库提交只有1次。我使用外挂缓存的效果和这完全一致。

两者的复杂程度也基本相当,Hibernate需要配置缓存策略,外挂缓存需多写几行代码。

而且这种极端情况还是很少见呀。

你还是没有仔细看我说的前提,一级缓存,不是二级缓存。如果还是这个例子,我自己做的话也会和你一样做一个键值对的缓存,这类缓存对应到Hibernate是二级缓存,跨事务的。

但是我都强调了,不是这种缓存,而且说了,这只是个例子,因为不是所有数据都能缓存的,有些情况不算是键值对,有些情况是数据量非常大,一级缓存在事务结束的时候都会释放内存的,我这里特地说了数据量非常大,你一启动就把所有14亿用户信息扔到缓存中,是想把JVM撑爆是吧。

还有我再提醒一下,即使内存够,JDK的LinkedHashMap在数据量超过10万以后就会有性能隐患,是不能和真正的缓存框架相提并论的。

缓存再简单都需要配置,hibernate的一级缓存是不需要配置的,不是二级缓存。

这种情况不少见,至少我经常碰到,我表明的意图就是,hibernate的一级缓存使用简单,默认就有,不需要任何配置,使得开发简单,更容易的复用事务脚本方法(例如findById之类的方法就可以很放心的被其他方法复用而不用担心性能问题)。这种情况和专门针对业务做的业务级缓存的概念是不一样的,这种在一个事务中的缓存技术是很实用的。
104 楼 wyuch 2009-06-10  
icewubin 写道
我再说一个Hibernate一级缓存的例子,为什么不说二级缓存,因为有争议,我也不推荐。

一级缓存和二级缓存最大的区别就是一级缓存只存在与一个事务中,事务结束,一级缓存也释放,这个功能是非常有用的。当一个业务逻辑很复杂的时候,你可以很放心的在一个事务中调用findById,而不用担心反复读取数据库。可能有人说,一个项目中80%都是CRUD,没那么复杂,所以我现在举个例子就是一个不复杂的业务查询中,一级缓存的价值。

假设一个管理页面上需要显示当前论坛最近发的20个帖子的信息,或者类似的需求,每一个帖子都需要显示该帖发帖人的姓名,假设数据库中两张表,帖子表和用户表,帖子表中存放了用户表的外键。有人看到这会说,用户ID和用户信息可以做缓存,JVM放不下,可以用memcached,这个就免了吧,我只不过举个例子,不是每种情况都适合做全局大规模缓存的。

再假设用户表中数据量很大,全中国14亿人吧,一人一条数据。帖子表,也没有给用户表外键做真的外键和索引。

好如果用JDBC如何实现(其实itatis和hibernate生成sql都一样)?
1.帖子表和用户表关联查询,因为我上面假设14亿人,帖子表中也没有物理外键或索引,所以这个两个表关联查询还是免了吧。

2.先一条sql查询帖子表最后10条记录,再20条sql查询用户表。啊,被逼的N+1了,呵呵,搞了这么多假设故意的,但是这些假设的前提在很多项目中是真实存在的。

继续说,如果是hibernate的话,不用join方式(两表关联方式)抓取数据,如果那20个用户都不一样,则也是N+1=21条sql,但是如果最后20帖中,是集中在8个人发的帖子,此时hibernate会命中一级缓存,最终就是8+1=9条sql。


我理解你的意思,但即便在你的假设前提下,不用Hibernate也很简单,使用外挂的支持LRU缓存(实际上可以用一个支持LRU的Hashmap,稍微改造一下LinkedHashMap就可以)缓存用户数据,效果完全一样。

其实你说的情况只会在JVM启动不久之后才会发生,系统运行一段时间后这8个人的数据都很有可能已经进入查询缓存了,虽然sql可能会有9次,但实际往数据库提交只有1次。我使用外挂缓存的效果和这完全一致。

两者的复杂程度也基本相当,Hibernate需要配置缓存策略,外挂缓存需多写几行代码。

而且这种极端情况还是很少见呀。
103 楼 icewubin 2009-06-10  
我再说一个Hibernate一级缓存的例子,为什么不说二级缓存,因为有争议,我也不推荐。

一级缓存和二级缓存最大的区别就是一级缓存只存在与一个事务中,事务结束,一级缓存也释放,这个功能是非常有用的。当一个业务逻辑很复杂的时候,你可以很放心的在一个事务中调用findById,而不用担心反复读取数据库。可能有人说,一个项目中80%都是CRUD,没那么复杂,所以我现在举个例子就是一个不复杂的业务查询中,一级缓存的价值。

假设一个管理页面上需要显示当前论坛最近发的20个帖子的信息,或者类似的需求,每一个帖子都需要显示该帖发帖人的姓名,假设数据库中两张表,帖子表和用户表,帖子表中存放了用户表的外键。有人看到这会说,用户ID和用户信息可以做缓存,JVM放不下,可以用memcached,这个就免了吧,我只不过举个例子,不是每种情况都适合做全局大规模缓存的。

再假设用户表中数据量很大,全中国14亿人吧,一人一条数据。帖子表,也没有给用户表外键做真的外键和索引。

好如果用JDBC如何实现(其实itatis和hibernate生成sql都一样)?
1.帖子表和用户表关联查询,因为我上面假设14亿人,帖子表中也没有物理外键或索引,所以这个两个表关联查询还是免了吧。

2.先一条sql查询帖子表最后10条记录,再20条sql查询用户表。啊,被逼的N+1了,呵呵,搞了这么多假设故意的,但是这些假设的前提在很多项目中是真实存在的。

继续说,如果是hibernate的话,不用join方式(两表关联方式)抓取数据,如果那20个用户都不一样,则也是N+1=21条sql,但是如果最后20帖中,是集中在8个人发的帖子,此时hibernate会命中一级缓存,最终就是8+1=9条sql。
102 楼 icewubin 2009-06-10  
wyuch 写道
大哥,我也没害怕,学习曲线还是有的,肯定不是10分钟的事情。我先有点事,一会来回你DDD的帖。

我没有骗你,只要SQL功底好,就是10分钟的事情。要么加个条件吧,学的人要知道hibernate的基本原理,配置文件或者注解映射对象和数据库,而且10分钟是入门常用功能,要学会hql的所有高级功能没必要,也用不着。

(实际情况是,学hibernate的新人大部分sql功底都不好,sql好的在我们公司都去数据仓库部门了,呵呵)
101 楼 wyuch 2009-06-10  
icewubin 写道
wyuch 写道
icewubin 写道
wyuch 写道
你不用HQL,也不用NativeSQL,只有面向对象的查询方式就可以完成开发?

完全可以,我们公司有项目就是这么做的。

虽然我还是推荐hql,并不是因为离不开hql,更主要的原因是hql的数据抓取功能(各种策略)太强大了。同时hql的学习难度也不是太大,毕竟和sql还是比较对应的,使用起来要比面向对象的查询方式更方便。


复杂的查询不用类似SQL的东西,太绕了,所以不用HQL还是过于特别了一点,一般都会用吧。

那我只能说,hql没有你想象的复杂,除了和sql对应的部分,注意事项10分钟就可以对你说完,你不要老是看到那些砖头书就害怕,没那么复杂,入门是很快的。

好比Spring的书有的也很厚,不是说书里面都是垃圾,而是常用的部分其实就一点点,其他东西大部分情况下用不到而已。


大哥,我也没害怕,学习曲线还是有的,肯定不是10分钟的事情。我先有点事,一会来回你DDD的帖。
100 楼 icewubin 2009-06-10  
wyuch 写道
icewubin 写道
wyuch 写道
你不用HQL,也不用NativeSQL,只有面向对象的查询方式就可以完成开发?

完全可以,我们公司有项目就是这么做的。

虽然我还是推荐hql,并不是因为离不开hql,更主要的原因是hql的数据抓取功能(各种策略)太强大了。同时hql的学习难度也不是太大,毕竟和sql还是比较对应的,使用起来要比面向对象的查询方式更方便。


复杂的查询不用类似SQL的东西,太绕了,所以不用HQL还是过于特别了一点,一般都会用吧。

那我只能说,hql没有你想象的复杂,除了和sql对应的部分,注意事项10分钟就可以对你说完,你不要老是看到那些砖头书就害怕,没那么复杂,入门是很快的。

好比Spring的书有的也很厚,不是说书里面都是垃圾,而是常用的部分其实就一点点,其他东西大部分情况下用不到而已。
99 楼 wyuch 2009-06-10  
icewubin 写道
wyuch 写道
你不用HQL,也不用NativeSQL,只有面向对象的查询方式就可以完成开发?

完全可以,我们公司有项目就是这么做的。

虽然我还是推荐hql,并不是因为离不开hql,更主要的原因是hql的数据抓取功能(各种策略)太强大了。同时hql的学习难度也不是太大,毕竟和sql还是比较对应的,使用起来要比面向对象的查询方式更方便。


复杂的查询不用类似SQL的东西,太绕了,所以不用HQL还是过于特别了一点,一般都会用吧。
98 楼 wyuch 2009-06-10  
zypro 写道
wyuch 写道

我不知道ORM概念始于何时,在你99年使用ORM三年后,微软发布.NET 1.0中没有这样技术门槛低的东西,1.1继续没有,2.0依然没有,以至于你所说的SPL之类的东西出现;同样在你使用JDK1.2之后的长达N年之后ORM都没有纳入J2EE的范畴,直到Hibernate等ORM实现己成气侯,才有JPA出现。所以你的所谓“实在是比较可笑”是不是先去笑J2EE或者.NET比较好?


我觉得可笑的是,在2009年6月份,有人把JDBC稍微封装一下,当成是新鲜事物来说. 美名其曰:"效率高","可以解决所有数据库开发中的问题".
wyuch 写道

有哪些是我的框架不能解决的?级联?导航?继承?对象一致性?可重复读?不能并发?因为就实际应用而言,还没有遇到不能处理的数据库开发的问题。

我明天去把汇编包装一下,写一篇文章:反思Java,可以更简洁,更高效的编程. 
节选如下:
我的方案代码简洁(无需申明任何类和方法,只需玩玩寄存器), 运行速度快(没有GC,没有间接内存分配,连虚拟机都不用,谁敢跟我比?),而且可以解决所有编程问题,多线程,网络,OO......(有什么是汇编干不了的吗?)

wyuch 写道

我的ORM框架要写的代码比Hibernate少,并且效率高,学习曲线短,我想这应该是没有太多疑问的。Hibernate功能强,但又有配制又有反射又有动态字节码,所以效率比我的差,我想这也是没有疑问的。

不懂Hibernate,就出来"反思"...
常识性的问题,封装层次高的,通常都会带来更简洁的代码.
之所以你觉得你的代码少了,两个原因,第一,你觉得用模板生成的代码不算代码. Hibernate的配置文件也可以用模板生成,或者使用Annotation几乎不用配置.所以实现那些你所谓的ORM,代码一定比你少. 第二,你的应用entity只和数据库打交道,保存一下只需一个save,所以你觉得代码少. 殊不知在业务层,如果domain设计的好,是可以用最简洁最OO的代码体现所有业务逻辑,Hibernate接管了所有持久化的操作,几乎都不用去显式调用数据读写的逻辑,你怎么可能做到更少的代码?
至于效率问题,讨论的够多了. 配置和反射,包括动态字节码,从来就不是一个应用的效率瓶颈. 没有听说过哪个数据库为中心的应用,是因为反射而影响了效率. 填写entity所花的时间,都比这个长......
Hibernate提供了orm级别的缓存,还有众多优化的手段. 请问你的orm能提供什么样的优化? 借助第三方的cache机制,得到的性能提升也算是你的框架的优势,实在是没话说... 小弟不才,要把第三方缓存用对,用好,自认不是那么容易... 当然,这个不是你框架的范畴,不会对你的"学习曲线低"这个优点构成影响.


你这就过于极端了,要不你包装汇编一下,帖上代码来比较一番?

[quote不懂Hibernate,就出来"反思"...
常识性的问题,封装层次高的,通常都会带来更简洁的代码.
之所以你觉得你的代码少了,两个原因,第一,你觉得用模板生成的代码不算代码. Hibernate的配置文件也可以用模板生成,或者使用Annotation几乎不用配置.所以实现那些你所谓的ORM,代码一定比你少. 第二,你的应用entity只和数据库打交道,保存一下只需一个save,所以你觉得代码少. 殊不知在业务层,如果domain设计的好,是可以用最简洁最OO的代码体现所有业务逻辑,Hibernate接管了所有持久化的操作,几乎都不用去显式调用数据读写的逻辑,你怎么可能做到更少的代码?

模板生成的代码里也只有Getter和Setter。不知道你怎么判断代码一定比我的少,我举了例子,
要不你也举个例子,特别是你说的设计的好的domain的例子,让我见识一下常识性的问题如何?我再用我的框架重写一遍,比较一下如何?

我的Cache是外挂的,跟ORM没关系,但不是第三方的,自己开发的,而且也从没说过从这里面得到了性能提高。你不要一方面总自己凭空臆测,另一方面又反过来说我没反思的权利。

我觉得按你在99年用ORM的经历,你基本上都是大哥,不会是小弟。
97 楼 icewubin 2009-06-10  
wyuch 写道
你不用HQL,也不用NativeSQL,只有面向对象的查询方式就可以完成开发?

完全可以,我们公司有项目就是这么做的。

虽然我还是推荐hql,并不是因为离不开hql,更主要的原因是hql的数据抓取功能(各种策略)太强大了。同时hql的学习难度也不是太大,毕竟和sql还是比较对应的,使用起来要比面向对象的查询方式更方便。
96 楼 icewubin 2009-06-10  
领域建模自身并不是和某一个特定的ORM的技术绑定的,假设DAO部分是JDBC来实现,而领域层完全按照OO的思想进行建模,在持久化这一层,既可以JDBC,也可以ibatis、hibernate,只要使用得当,这三者最终产生的SQL最终是一样的。

我举个实际的例子:删除一个班级。班级(Classes)对学生(Student)一对多,现在假设一个班级下有四十个学生,学生表中有班级表的外键。

1.如果是面向sql的编程,很简单,参数是一个班级ID,一个事务两条sql,先删除学生表中40条数据,再删除班级表中1条数据。这是面向sql的编程思路,很多人以为只能jdbc、ibatis做起来方便,hibernate会很麻烦,其实不然,hibernate3做起来一样是可以超简单的,两条hql就搞定了。

2.如果是根据领域模型设计的话,看你如何设计了,先假设,学生不能独立于班级单独存在,领域设计时认为40个学生和班级是一个“聚合”体,如果是这样的思路,体现在hibernate中就是学生的model会有指向班级多对一的配置(肯定会有,数据库中学生表中是真实存在班级的外键的),班级的model中配置了指向学生的一对多配置,同时配置级联。

然后分析执行的时候会产生哪些sql(先不考虑延迟加载),读取的时候如果配置得当,产生两条sql,读取班级表和学生表的相关记录,在内存中构造出一个班级附带40个学生的“聚合”实体对象图。然后执行session.delete(classes),根据级联配置,产生40条删除学生表数据的sql语句和1条删除班级表数据的sql语句,都是根据ID来删除的。

到这里大家想一下,不考虑针对删除优化的话,领域模型如此设计,持久化技术无论是JDBC还是ibatis,整个过程中产生的sql是和hibernate一样的,不会多也不会少。也就是说,这个复杂度是由领域模型设计造成的,是领域模型和关系数据库阻抗不匹配造成的,原因并不是ORM软件本身。

这里岔出去说两点。
1)很多大型项目不使用hibernate,有一个原因是DBA不高兴,说他要优化sql的执行不方便,所以DBA们更喜欢ibatis。
2)如果学生表是海量数据的话,例如是全国所有的学生表(实际不太可能,举个例子而已),上述的40条删除语句也是能够接受的,因为根据ID来做一个一个的删除,速度还是很快的,至少不会随着表中数据的增加而变慢。有人一定会说,40条删除语句肯定比根据班级外键删除来的慢,实际不一定,这个外键可能是个逻辑外键,实际生产环境如果出于学生表插入性能的考虑,不做物理上的外键,同时也不对班级ID这个字段做索引,一旦海量数据的时候,根据班级ID删除学生虽然是一条sql,性能反而是得不到保证的,因为会扫描全表。而40条删除语句虽然是40条,但是主键一定是有索引的。

3.继续说领域模型设计的问题,在第二点中说班级和学生是聚合体,但是实际往往不是这样,一个学校使用一个信息管理系统中,一定会有针对学生的管理和检索,例如一个学生的信息修改、一个学生换班级、检索出所有的本学期需要补考的学生列表,业务模型的焦点指向了学生,所以一般来说,把学生和班级设计成“聚合体”是不合适的,学生对象本身就是一个实体,该实体的导航不依赖于班级实体。

所以如果是用hibernate配置,就不要在班级model中配置一对多的映射(但是那个学生集合还是可以存在),或者是配了一对多但不要配任何级联操作(即使如此,还是不建议配置一对多)。这样,最终实现的时候,就会有一个单独的studentRespository(或studentDao,假设是事务脚本模式,贫血模型),再加上原来就应该存在的classesRespository(或classesDao),那么在classesService中的删除操作往往会这样操作:
public Classes findById(String classesId) {
    Classes classes = classesRespository.findById(classesId);
    //如果没有配一对多的话,多下面这一句
    classes.setStudents(studentRespository.findByClassesId(classesId));
    return classes;
}
public void delete(Classes classes) {
    studentRespository.deleteStudents(classes.getStudents);//多这一行
    classesRespository.delete(classes);
}

虽然看起来,查询和删除的时候要麻烦点,其实就各多写一句话而已,再由于单DAO设计,studentRespository和classesRespository的基本CRUD都是很简单的,studentRespository的findByClassesId方法实现也很简单,如下:
public Set findByClassesId(String classesId) {
    String hql = "from Student stu where stu.classes.id=?";
    return super.findList(hql, classId);//一般单DAO的父类支持,尤其是第二个参数形式可以多种
}

那么在这个例子中,因为studentRespository是单独存在的,如果需要做删除优化,也不复杂,studentRespository.deleteStudents想怎么实现都可以(Hibernate有批处理删除hql的)。studentRespository中会针对学生实体的需求,会有很多其他方法。
第三点设计说着了这么多,并不是说为了个删除优化,搞出这么多事,而是根据需求来设计的结构,学生表和班级表不再是聚合体了。此时也会有人问,这领域模型设计有啥稀奇的,最后还不是搞得和自下而上的表模式生成的model一模一样?当然不一样,一旦真的有业务上的聚合体存在,领域模型设计出的类和表模式自下而上设计出的类就是不一样的,领域模型和物理模型(ER模型,数据库设计)还有很多其他方面的差别,例如学生领域中可能会有一个地址对象,而数据库设计中仅仅是学生表中多几个字段,并没有地址表。

写了这么多,希望对大家有点帮助,其中的重点在于以hibernate中容易出性能问题的一对多为例,阐述一个例子,来说明这更多的是领域建模中固有的复杂度(阻抗不匹配),领域建模中本身也是有一定的针对关系数据库的建模技巧。同时说明hibernate本身是没有性能问题的,领域建模不当(ORM使用不当)导致的各种性能问题和ORM软件本身没有直接关系。
95 楼 wyuch 2009-06-10  
icewubin 写道
wyuch 写道
是的,你说的都对,但我不用这些机制,而且不需要解析HQL,直接封装的JDBC,就算比Hibernate好得有限,也还是要好些的。另一个方面有点优势的是性能出现问题的话,我不需要怀疑我的ORM,他就这么简单,本身不构成性能问题,完全出自自己的团队,很好掌握,这算是自主开发的一点额外好处吧。

1.你说这些话的前提就是认为Hibernate有性能问题,有么?你听到的性能问题都是ORM使用不当导致的,例如N+1查询问题。这个问题分两个方面说,一方面Hibernate可以弃用大多数高级特性,只用少数功能,那就和你的框架接近了,另一方面无论哪种DAO(ORM或者自己封装),使用不当都会导致N+1的这类问题,这和框架本身无关,不是框架的性能问题,而是框架的使用问题。

2.自主开发当然有好处也有坏处,问题在于你说的相对于Hibernate的好处本来就不存在,你说的那些问题hibernate本身是没有的,最多平手而已。我再多说一个学习成本的问题,只要限定Hibernate高级特性的使用范围,再给几个例子,hibernate的培训也是很简单的,所以你说的学习成本也是没有可比性的,因为hibernate本身是足够灵活的,既可以面向OO编程,也可以面向SQL编程,如果只使用部分功能,何必去学那些整本书的hibernate教程呢。还有HQL的问题,hibernate可以不依赖于hql,之前说过了,面向对象的查询方式QDC、QBDC,基于Example的快捷查询QBE也很方便的。楼主还是多了解一下hibernate再来发表见解比较好。

3.至于代码生成还是自上而下或者自下而上,和hibernate本身没有直接关系,属于第三方代码生成工具而已。我提这个,原本是想楼主了解一下领域驱动设计的概念,再回过头来想ORM的作用。


1、其实我说了两个意思,第一是机制比Hibernate简单,所以效率高。第二是复杂框架可能会带来潜在的性能问题,我想一个成熟的框架本身不会直接有问题,不然也生存不下来,但使用它有可能带来性能问题,并且出现性能问题时追踪不易。
2、Hibernate的学习曲线如何,我想大家还是清楚的,我也学过,虽然最终没有深入使用,但知道并不简单。有没有有可能是你熟悉之后回头觉得比较轻松?
3、你不用HQL,也不用NativeSQL,只有面向对象的查询方式就可以完成开发?
4、其实即便最多平手,又或者说要可以弃用大多数高级特性的话,那我还是倾向于一个可以掌握所有实现细节的简洁ORM。
94 楼 kakashishi 2009-06-10  
如果hibernate使用不当 那么在高并发时很容易出现内存泄露的问题
93 楼 kakashishi 2009-06-10  
hibernate主要是耗资源,如果不怕耗那么使用它可以为你提升一定的性能。
92 楼 jnoee 2009-06-10  
强制要求实体类去继承一个Schema类,这一点就是一个最大的硬伤。以这种思路,只需要对hibernate做少少封装就可以实现楼主所演示的那些功能。

相关推荐

    Hibernate框架ORM的实现原理

    ### Hibernate框架ORM的实现原理详解 #### 一、ORM概念及意义 **ORM**,即**对象关系映射**(Object Relational Mapping),是一种程序...了解Hibernate的实现原理有助于更好地运用这一框架,解决实际开发中的问题。

    Hibernate orm 实现原理

    Hibernate orm 实现原理 主要讲解了关于hibernate 的一些知识

    hibernate-orm-master.zip

    在实际开发中,理解并熟练运用这些核心概念和机制,可以帮助我们更高效地利用Hibernate ORM进行数据访问层的设计,减少数据库操作的复杂性,提高代码的可维护性。对于初学者,建议从简单的JAR包开始,逐步熟悉其API...

    HibernateORM

    Hibernate作为Java领域广泛使用的ORM框架,它极大地简化了数据库操作,将面向对象的编程思想与关系型数据库相结合,使得开发者可以更加专注于业务逻辑,而不是繁琐的SQL语句。在本书中,作者深入浅出地讲解了如何...

    hibernate-orm-3.3源码

    总结,通过分析《hibernate-orm-3.3源码》,我们可以深入理解 Hibernate 的工作机制,掌握如何高效地使用 ORM 技术,以及如何根据需求扩展和定制 Hibernate。对于任何想提升数据库操作效率和代码可维护性的 Java ...

    ORM hibernate。jar包

    标题提到的"ORM Hibernate .jar包"指的是Hibernate框架的可执行库文件,通常包含了一系列类、接口和实现,供开发者在项目中引用,以实现ORM功能。"hibernate5+jars包"表明这是Hibernate的第五个主要版本,5.2.16....

    hibernate-orm-4.3.9源码

    本文将基于从Hibernate官网下载的hibernate-orm-4.3.9源码,深入解析其内部机制,帮助开发者更好地理解和应用Hibernate。 一、Hibernate概述 Hibernate的核心功能是将Java对象与数据库表进行映射,实现了数据持久...

    Hibernate_ORM步骤详解

    Hibernate ORM 是一种强大的Java持久层框架,它实现了对象关系映射(ORM)的概念,使得开发者可以使用面向对象的方式来操作数据库,而无需关心底层SQL语句的编写。在本教程中,我们将详细介绍如何利用Hibernate 3...

    hibernate-orm-3.2.zip

    Hibernate ORM 是一个强大的Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的数据交互...虽然现在Hibernate已经发展到了更高级的版本,但理解3.2的基础知识仍然有助于理解ORM的本质和工作原理。

    hibernate-orm-master

    hibernate-orm-master

    hibernate-search-orm-4.4.2.Final.zip

    总结来说,Hibernate Search ORM 4.4.2.Final是Java开发中实现全文检索的强大工具,而AtomicMapOperations则提供了并发映射的非阻塞原子操作,两者结合可以构建出高性能、高并发的搜索应用。对于任何处理大量数据和...

    hibernate-framework-orm-4.2.4.Final.zip

    Hibernate作为ORM工具,主要解决了Java应用与数据库之间的数据交互问题,通过将数据库表映射为Java类,实现对象与数据的自动转换。这使得开发者可以使用面向对象的方式来操作数据库,降低了数据库操作的复杂性。 2...

    自定义Orm框架的实现

    本项目旨在实现一个基于JDK5.0注解的小型ORM框架,模拟Hibernate的部分功能。 首先,我们要理解ORM的基本原理。ORM的核心思想是将数据库中的表映射为Java对象,将表中的记录映射为对象的实例,这样就可以通过操作...

    org.springframework.orm.hibernate3.LocalSessionFactoryBean

    ### 关于 "org.springframework.orm.hibernate3.LocalSessionFactoryBean" 未找到问题的知识点解析 #### 一、问题背景 在开发基于Spring与Hibernate整合的应用时,可能会遇到“`org.springframework.orm.hibernate...

    Hibernate ORM - 一对多双向关联关系

    在Hibernate中,这种关系可以通过`@OneToMany`注解实现。例如,User类可能会有如下注解: ```java @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; ...

    Hibernate源码(hibernate-orm-main.zip)

    Hibernate源码(hibernate-orm-main.zip)Source Code: Hibernate ORM 是一个为应用程序、库和框架提供对象/关系映射 (ORM) 支持的库。 它还提供了 JPA 规范的实现,这是 ORM 的标准 Java 规范。

    ORM的简单介绍及相应ORM工具Hibernate的使用规则

    对象关系映射的概念,及相应Hibernate的使用规范,同时通过实例展示到底什么是对象关系映射。

    Hibernate入门到精通

    Hibernate 是一个基于Java的ORM(Object-Relational Mapping,对象关系映射)框架,它提供了一种简洁高效的方式来访问和操作关系数据库。下面是 Hibernate 的主要知识点: Hibernate 简介 Hibernate 是一个开源的...

Global site tag (gtag.js) - Google Analytics