对ORM JPA设计上的几个疑问
??? 近来做JPA的simple时,不时遇到了几个疑问,我不知为什么JPA的设计者要这样设计一个ORM。
- ?为什么jpa的merge,要首先去数据库找一下数据,后等到事务提交方生成update的的脚本,我还因此以为自己做的unitTest出错。先去查一下数据库这一步,为什么不能略去呢,虽然在大部分情况下,他应该能在二级缓存中找到这条数据,但,我还是实在看不出理由来为什么删除一条数据,一定要受控状态下的实体。这就照成了要删除一条记录时,需要再去查询一次,获得一个受控实体后,方能删除。这又是为什么,我怎么一点也没有感觉到它的好处呢。
?
6 楼 nihongye 2008-07-01 guoshiguan 写道我知道merge操作主要目的,但为什么要查找了,是设计二级缓存时必要的吗,为什么不直接更新数据库就好,我实在不觉复查找是个正常的事,可能是因为我对二级缓存的实现方式还不了解吧。
时相关事件的触发,级联删除,缓存更新都依赖于受管对象,所以要先有受管对象。
因为事实jpa的设计就不想即刻做数据库同步,merge后,仍可以更改对象的状态,只在主动flush或auto flush需要时去同步。另外级联merge时,需要知道那些关联对象需要merge,这也需要知道对象的状态,所以需要获取对象的状态(也即是受管对象),虽然受管对象可能跟数据库不一致,但这一点不影响,不一致情况下后面的更新检查抛出异常,或事务提交异常。删除也同理。
另外如hibernate的事务性和readwrite二级缓存设计,在事务成功时,对事务中所发生变化的受管对象进行更新。
举个merge的例子:
Student{
@OneToMany
List books
}
加入一detached的books有books1,books2,而受管对象有books1,books2,book3
那么要如何做数据库同步呢?
第一种方式:
update Book set studentId = '' where studentId = 'abc',
update Book set student = 'studentId' where id = 'booksId1'
update Book set student = 'studentId' where id = 'booksId2'
或是只做一次
update Book set student = null where id = 'booksId3'
二级缓存又如何将book3与student的关系去除呢?
从设计上说,符合以下条件,可只产生update语句:
1.只有单向关系。
2.二级缓存的设计使用更新则清除。
3.id产生策略采用自动方式(因为jpa规定merge时需要检查对象是否存在)。
7 楼 Joo 2008-07-02
引用
我要用脱管对象里的ID用JPA的find方法获得一个受控对象,再来对这个对象进行删除,这整个流程怎么都会让人觉得很奇怪吧。
ORM框架说:你都不告诉俺你要删哪个? 我怎么知道删哪个?
8 楼 icewubin 2008-07-04 nihongye 写道有 自动同步,级联,懒加载,缓存,事件这些优点。
因为Hibernate也实现了JPA,我就拿Hibernate说事了。
除了懒加载和部分事件,没看出其他东西有什么用途,基本上都是和性能调优对立的,我认为放弃那些特性也没什么不好的。
还有就是缓存,除了一级缓存还有点用以外,二级缓存基本无用,即使用了,效率还不如自己控制全局的缓存控制来的灵活,全局的控制还能用上更多的其他缓存技术,而不是局限在Hibernate中,局限在Java的JVM中(Memcached,不是缓存页面)。
托管对象,造成巨大的Hiberanate学习成本,28原则,Hibernate中80%的陡峭学习曲线就是由这些20%的东西造成的,然后在性能调优上更是浪费不少时间,这和原本提高整个效率的做法背道而驰的,增加设计成本、增加调试成本、增加维护交接成本。
有这点时间,几行编码早就解决了,比如级联就是典型例子。随着对Hibernate的理解越深刻,越发现由级联、托管对象、二级缓存越是太理想化。 9 楼 icewubin 2008-07-04 guoshiguan 写道guoshiguan 写道
merge操作主要目的将非受管对象的状态同步到受管对象中。受管对象构造过程session-》二级缓存-》数据库,发生数据库查找很正常。
我知道merge操作主要目的,但为什么要查找了,是设计二级缓存时必要的吗,为什么不直接更新数据库就好,我实在不觉复查找是个正常的事,可能是因为我对二级缓存的实现方式还不了解吧。
guoshiguan 写道时相关事件的触发,级联删除,缓存更新都依赖于受管对象,所以要先有受管对象。
受管对象的好处要当然知道,但问题是,为什么删除一定要受管对象,级联删除为什么会依赖于受管对象呢。
guoshiguan 写道你可能最想弄明白受管对象有什么好?
有 自动同步,级联,懒加载,缓存,事件这些优点。
我不是不明白受管对象的好处,我只是不明白,为什么直接做的事,为什么删除操作要变成受管对象以后才能做。
很简单,只要存在二级缓存的可能,那更新和删除对象,势必要先对比二级缓存中的数据是否需要同步。
你问得很好,完全可以不用级联、二级缓存、merge这些功能,没有必要。
我认为只有在一个事务的范围中,hibernate才有利用价值(比如一级缓存),而且要充分利用。跨事务的话,除了open session in view,其他的都免了吧。
10 楼 nihongye 2008-07-04 说明一句:不管你用的是java cache产品还是非jvm的cache产品,实现对象粒度并具有事务性的缓存策略是orm这些框架的事,具体的缓存产品只是一个数据的存取位置。
我认为这些策略正是处理了事务性应用数据持久化方面80%的问题。
剩下的0.2,呵呵,比如,让只读事务使用FlushMode.NEVER,数据量大的,自己控制flush,减少sql语句数量,数据量达到构造过多的对象都成为瓶颈,那就不要构造对象了, 11 楼 icewubin 2008-07-04 nihongye 写道说明一句:不管你用的是java cache产品还是非jvm的cache产品,实现对象粒度并具有事务性的缓存策略是orm这些框架的事,具体的缓存产品只是一个数据的存取位置。
我认为这些策略正是处理了事务性应用数据持久化方面80%的问题。
剩下的0.2,呵呵,比如,让只读事务使用FlushMode.NEVER,数据量大的,自己控制flush,减少sql语句数量,数据量达到构造过多的对象都成为瓶颈,那就不要构造对象了,
我认为对象缓存要具有事务性这一点本身就是有问题的,这只会把问题越搞越复杂。一般要考虑到这种级别的缓存策略时,个人倾向与这两个最好分开,大规模的缓存不要牵涉到事务,否则达不到大幅度提升效率的效果。或者说这个时候更应该花精力在业务上,来进一步减少事务的长度,降低系统逻辑对事务的过度依赖,因为很多时候有些事务是没必要的。
cache我没说清楚,我的意思是一级缓存是有价值,ORM的二级缓存从整体上来看就是效率不高的,虽然比没有好,但是仍然是比较差的,自己稍加设计一个简单的对象缓存就能更大幅度的提高运行效率(当然是有这个的需求下,比如针对动态数据字典的缓存设计)。
我说的80%和你说的不是一回事,我说的是由使用了级联或者是跨事务的功能,例如merge产生的。这一点从一般的示例代码中是看不出的,表面上看级联提供了很好的封装,但是实际使用就会发现很多很诡异的现象,究其根本是不是每个人都深刻掌握了级联各种配置的实现原理,然后级联操作本身也没有对二级缓存做完全的同步,自己还是要写些额外代码。进一步,如saveOrUpdate()起不到提高效率的作用,反过来还降低代码可读性(谁有疑问,我给你们举详细例子),像all-delete-orphan这种强耦合要做业务级的重构是很不方便的,多对多级联的操作也是很难优化性能的,集群下跨事务的操作往往更多的带来的是性能的浪费,而不是减少数据库操作,这和SNA思想也是有点背道而驰的。当然有少数业务场景是需要这种技术的,比如类似于ATM机的功能,但是实际呢?国内银行往往用的还是老的技术,美其名曰保险起见,比如他们不相信数据库保证的JTA两段式提交,大多还是用的补偿机制,自己变相的做同步,当然造成这样的原因也有很多银行自己的问题(比如数据库太老,网管不肯开XA等)。
事务控制一般用的都是Spring的,虽然底层也调用了hibernate,但是单就事务传播这个特性上来讲,spring对减少复杂度提供的贡献是要超过hibernate的。
我的意思是Hibernate这类ORM提供了假设是100%的功能,但是其中有些很复杂,要花80%的时间去学习、去培训其他人、去调优,不值得。不如弃用这部分功能,就把Hibernate当作一个高级的Sql生成器。
既然要Sql高手(包括sql调优、数据抓取策略、数据库设计)才能用好Hibernate,还不如把Hibernate当作一个Sql生成器,也比把他当作所谓的ORM要好。
12 楼 nihongye 2008-07-04 我说的也是二级缓存的设计,在事务环境下,设计一个正确的对象二级缓存是件很难的事情。
二级缓存的cache问题,我遇到的一个:hibernate二级缓存对双向关系的处理,必须保证两个方向的一致性,否则,cache存在脏数据,这个问题不被hibernate的团队所接受,所以只能作为一个原则比接受了。你说的多对多关系,list,或者是很复杂的继承关系,奇怪的映射方式,我也是弃用的原则,中庸的使用hibernate,相比jpa做了很多简化。
无论是否有hibernate,只要涉及大数据量,免不了要懂得sql的优化 13 楼 linux.sir 2008-07-04 感觉用ibatis 来得爽 14 楼 icewubin 2008-07-06 linux.sir 写道感觉用ibatis 来得爽
两个问题:
1.ibatis如何解决动态创建sql的烦恼,有没有很好的解决方案,我看我们公司用ibatis的都使用配置脚本逻辑来动态拼sql,太原始了吧。
2.既然以sql为主,为何不用存储过程,难道sql语句放在配置文件中就比放在数据库中好维护么? 15 楼 rocwon 2008-07-07 icewubin 写道linux.sir 写道感觉用ibatis 来得爽
两个问题:
1.ibatis如何解决动态创建sql的烦恼,有没有很好的解决方案,我看我们公司用ibatis的都使用配置脚本逻辑来动态拼sql,太原始了吧。
2.既然以sql为主,为何不用存储过程,难道sql语句放在配置文件中就比放在数据库中好维护么?
Ibatis写起SQL来特别是INSERT, UPDATE, DELETE,一行一行的,也比较烦.动态SQL只能是在配置文件里用逻辑标签拼出来.
俺从IBATIS转向OPENJPA了。 16 楼 Joo 2008-07-07 icewubin 写道linux.sir 写道感觉用ibatis 来得爽
两个问题:
1.ibatis如何解决动态创建sql的烦恼,有没有很好的解决方案,我看我们公司用ibatis的都使用配置脚本逻辑来动态拼sql,太原始了吧。
2.既然以sql为主,为何不用存储过程,难道sql语句放在配置文件中就比放在数据库中好维护么?
每个产品的出现肯定不会重蹈之前产品覆辙,所以ibatis也肯定不会跟sql一样,以sql为主这个概念被你偷换掉了.说到底,ibatis还是一个具有ORM功能的sql script解决方案.所以编写sql语句只能算是它的工作方法,而不是他的工作目的.看新事物要看它新在哪里,而不是拿长比短 17 楼 icewubin 2008-07-08 Joo 写道icewubin 写道linux.sir 写道感觉用ibatis 来得爽
两个问题:
1.ibatis如何解决动态创建sql的烦恼,有没有很好的解决方案,我看我们公司用ibatis的都使用配置脚本逻辑来动态拼sql,太原始了吧。
2.既然以sql为主,为何不用存储过程,难道sql语句放在配置文件中就比放在数据库中好维护么?
每个产品的出现肯定不会重蹈之前产品覆辙,所以ibatis也肯定不会跟sql一样,以sql为主这个概念被你偷换掉了.说到底,ibatis还是一个具有ORM功能的sql script解决方案.所以编写sql语句只能算是它的工作方法,而不是他的工作目的.看新事物要看它新在哪里,而不是拿长比短
少写几个字就被你说成“偷换概念”了?我说的sql为主,是指业务逻辑的所在,也包括“两个问题”的第一个。
真的要说拿长比短,我还没拿Hibernate中的原生sql支持或Spring的JDBCTemplate和iBatis比呢。
那你说说iBatis新事物,新在哪里?他的工作目的何在呢?长在哪里,短在哪里?
还有,我还没提iBatis的长短问题呢?我只是说了我对iBatis的一些困惑,请不要只说什么“长短”,多说点实际使用过程中,如果碰到我提到的两个困惑,一般是如何考虑的。 18 楼 Joo 2008-07-08 引用真的要说拿长比短,我还没拿Hibernate中的原生sql支持或Spring的JDBCTemplate和iBatis比呢。
Hibernate为什么会有支持native Sql的功能呢,就是因为有些事情还是需要sql(或者native sql)来做比较好啊,每个产品无不都是在灵活性和简便性之间求平衡,Hibernate偏左,ibatis偏右而已阿!!不同的侧重本身就是他们出生的目的
对于上面的两个问题:
1 写死,动态sql用的并不多的
2 不明白你说的以sql为主是做何解.
19 楼 qjzhyf 2008-07-08 初学JPA,关于实体之间的关系和级联问题,唉,感觉这些理解起来太难了。LS的大大们,都有些什么好经验啊。多给我们提提呗。 20 楼 icewubin 2008-07-08 Joo 写道引用真的要说拿长比短,我还没拿Hibernate中的原生sql支持或Spring的JDBCTemplate和iBatis比呢。
Hibernate为什么会有支持native Sql的功能呢,就是因为有些事情还是需要sql(或者native sql)来做比较好啊,每个产品无不都是在灵活性和简便性之间求平衡,Hibernate偏左,ibatis偏右而已阿!!不同的侧重本身就是他们出生的目的
对于上面的两个问题:
1 写死,动态sql用的并不多的
2 不明白你说的以sql为主是做何解.
我的意思是Hibernate对原生sql的支持某些方面是很好的,我说的“原生sql的支持”并不是指iBatis的这种支持力度,我认为iBatis对sql语句的动态变化的控制能力是很弱的。你也说了,写死,问题是很多项目有这个需求,这个sql就是要动态参数(是参数的个数,不是参数数值)的,怎么可能写死呢?写死的话应用场景太狭窄了吧。
我说的以sql为主的意思就是想问你,既然把sql放在配置文件里,为什么不放在数据库中作为存储过程来调用呢?
存储过程的话,执行效率高,还不需要通过网络传输各条sql,不是更好么?
21 楼 java.lang.Object 2008-07-12 我刚才测了一下调用JPA和JDBC,发现插入速度都差不多哦,并且我是用Remote的session bean来调用的,并且不在同一个JVM下面,其实现在JPA实现已经挺不错了,我用的实现是toplink的 22 楼 raolei660528 2009-04-09 太高深了。。。看不懂。。。。 23 楼 afadgaeg 2009-04-19 因为“傻数据库”
所有的数据完整性都由orm维护,所以在执行任何数据持久化操作前,orm必须知道数据的状态,否则orm不能保证执行完整性级联操作 24 楼 小豆冰 2009-04-22 afadgaeg 写道因为“傻数据库”
所有的数据完整性都由orm维护,所以在执行任何数据持久化操作前,orm必须知道数据的状态,否则orm不能保证执行完整性级联操作
正解。
顺便问一下大家在实际项目中用了多少级联?
个人感觉级联在访问量稍微大点的系统中造成了很多问题,像scalability, availability, concurrency. 最近接手一个外包留下的项目, 里面大量用了级联,很多垃圾级联造成了严重性能问题和由Hibernate优化锁造成的死锁问题。 25 楼 kjj 2009-04-22 尽量不用级联,级联的麻烦比较多!!