本站首页    管理页面    写新日志    退出

«August 2025»
12
3456789
10111213141516
17181920212223
24252627282930
31


公告

  如果你忍了,欺负你的人将来可能就进监狱了。如果你反击,欺负你的人将来可能就获选十大杰出青年了。

        QQ: 3159671

http://greenboy.javaeye.com/

http://blog.sina.com.cn/u/1278341164 小鸟吹烟


我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:小鸟吹烟
日志总数:157
评论数量:424
留言数量:-1
访问次数:1253353
建立时间:2006年10月23日




[Hibernate 专区]inverse=true
文章收藏,  网上资源

tone 发表于 2007/4/1 18:45:53

建议大家好好去看一下这个链接,讲的很详细,虽然是E文,可并不难懂。 http://www.hibernate.org/155.html 这篇文章里没有提到delete的情况,我就one-to-many来做个简单说明: 代码 Parent parent = (Parent) session.load(Parent.class, pid);    session.delete(parent);    1. 爸爸那边设置了cascade=all和inverse=true a. delete from children b. delete from parent 2. 爸爸那边设置了cascade=all,没有设inverse=true a. update children set parent_id=null where parent_id=? b. delete from children c. delete from parent 因为此时inverse=false,所以爸爸要负责维护relationship,所以它要去把children中的连接信息都清空。但是,如果children的parent_id的constraint设置的是not-null的话,那么很不幸,hibernate执行到2.a就会throw exception了。 如果你要问,这个inverse=true到底是在源代码中的哪儿判断的呀?请看代码: 代码 ......       SessionImpl.flush();       SessionImpl.execute();       SessionImpl.executeAll(collectionRemovals);        executable.execute();       ScheduledCollectionRemove.execute();        getPersister().remove( getId(), getSession() );       OneToManyPersister(即AbstractCollectionPersister).remove();        if ( !isInverse ) {            PreparedStatement st = session.getBatcher().prepareBatchStatement( getSQLDeleteString() );        }       OneToManyPersister.getSQLDeleteString();        return "update children set parent_id=null where parent_id=?";       ......   


阅读全文(10488) | 回复(4) | 编辑 | 精华
 


回复:inverse=true
文章收藏,  网上资源

dfg(游客)发表评论于2009/12/10 9:15:35

dfsgsdgdfgsdfgsgsg


个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:inverse=true
文章收藏,  网上资源

tone发表评论于2007/4/1 19:10:51

在多對一、一對多中都是單向關聯,也就是其中一方關聯到另一方,而另一方不知道自己被關聯。 如果讓雙方都意識到另一方的存在,這就形成了雙向關聯,在多對一、一對多的例子可以改寫一下,重新設計User類別如下: User.java package onlyfun.caterpillar; public class User { private Integer id; private String name; private Room room; public User() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Room getRoom() { return room; } public void setRoom(Room room) { this.room = room; } } Room類別如下: Room.java package onlyfun.caterpillar; import java.util.Set; public class Room { private Integer id; private String address; private Set users; public Room() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Set getUsers() { return users; } public void setUsers(Set users) { this.users = users; } public void addUser(User user) { users.add(user); } public void removeUser(User user) { users.remove(user); } } 如此,User實例可參考至Room實例而維持多對一關係,而Room實例記得User實例而維持一對多關係。 在映射文件方面,可以如下撰寫: User.hbm.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="onlyfun.caterpillar.User" table="user"> <id name="id" column="id" type="java.lang.Integer"> <generator class="native"/> </id> <property name="name" column="name" type="java.lang.String"/> <many-to-one name="room" column="room_id" class="onlyfun.caterpillar.Room" cascade="save-update" outer-join="true"/> </class> </hibernate-mapping> Room.hbm.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="onlyfun.caterpillar.Room" table="room"> <id name="id" column="id"> <generator class="native"/> </id> <property name="address" column="address" type="java.lang.String"/> <set name="users" table="user" cascade="save-update"> <key column="room_id"/> <one-to-many class="onlyfun.caterpillar.User"/> </set> </class> </hibernate-mapping> 映射文件雙方都設定了cascade為save-update,所以您可以用多對一的方式來維持關聯: User user1 = new User(); user1.setName("bush"); User user2 = new User(); user2.setName("caterpillar"); Room room1 = new Room(); room1.setAddress("NTU-M8-419"); user1.setRoom(room1); user2.setRoom(room1); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(user1); session.save(user2); tx.commit(); session.close(); 或是反過來由一對多的方式來維持關聯: User user1 = new User(); user1.setName("bush"); User user2 = new User(); user2.setName("caterpillar"); Room room1 = new Room(); room1.setUsers(new HashSet()); room1.setAddress("NTU-M8-419"); room1.addUser(user1); room1.addUser(user2); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(room1); tx.commit(); session.close(); 這邊有個效率議題可以探討,上面的程式片段Hibernate將使用以下的SQL進行儲存: Hibernate: insert into room (address) values (?) Hibernate: insert into user (name, room_id) values (?, ?) Hibernate: insert into user (name, room_id) values (?, ?) Hibernate: update user set room_id=? where id=? Hibernate: update user set room_id=? where id=? 上面的程式寫法表示關聯由Room單方面維持,而主控方也是Room,User不知道Room的room_id是多少,所以必須分別儲存Room與User之後,再更新user的room_id。 在一對多、多對一形成雙向關聯的情況下,可以將關聯維持的控制權交給多的一方,這樣會比較有效率,理由不難理解,就像是在公司中,老闆要記住多個員工的姓名快,還是每一個員工都記得老闆的姓名快。 所以在一對多、多對一形成雙向關聯的情況下,可以在「一」的一方設定控制權反轉,也就是當儲存「一」的一方時,將關聯維持的控制權交給「多」的一方,以上面的例子來說,可以設定Room.hbm.xml如下: Room.hbm.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="onlyfun.caterpillar.Room" table="room"> <id name="id" column="id"> <generator class="native"/> </id> <property name="address" column="address" type="java.lang.String"/> <set name="users" table="user" cascade="save-update" inverse="true"> <key column="room_id"/> <one-to-many class="onlyfun.caterpillar.User"/> </set> </class> </hibernate-mapping> 由於關聯的控制權交給「多」的一方了,所以直接儲存「一」方前,「多」的一方必須意識到「一」的存在,所以程式片段必須改為如下: User user1 = new User(); user1.setName("bush"); User user2 = new User(); user2.setName("caterpillar"); Room room1 = new Room(); room1.setUsers(new HashSet()); room1.setAddress("NTU-M8-419"); room1.addUser(user1); room1.addUser(user2); // 多方必須意識到單方的存在 user1.setRoom(room1); user2.setRoom(room1); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(room1); tx.commit(); session.close(); 上面的程式片段Hibernate將使用以下的SQL: Hibernate: insert into room (address) values (?) Hibernate: insert into user (name, room_id) values (?, ?) Hibernate: insert into user (name, room_id) values (?, ?) 如果控制權交給另一方了,而另一方沒有意識到對方的存在的話會如何?試著將上面的程式片段中user1.setRoom(room1);與user2.setRoom(room1);移去,執行之後,您會發現資料庫中room_id會出現null值,這種結果就好比在多對一中,您沒有分配給User一個Room,理所當然的,room_id會出現null。

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:inverse=true
文章收藏,  网上资源

tone发表评论于2007/4/1 18:49:18

http://hi.baidu.com/injava/blog/item/acb67b2214c3c3a44723e84f.html 1、到底在哪用cascade="..."?         cascade属性并不是多对多关系一定要用的,有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除,所有 cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade,unsaved-value是个很重要的属性。Hibernate通 过这个属性来判断一个对象应该save还是update,如果这个对象的id是unsaved-value的话,那说明这个对象不是 persistence   object要save(insert);如果id是非unsaved-value的话,那说明这个对象是persistence   object(数据库中已存在),只要update就行了。saveOrUpdate方法用的也是这个机制。         2、到底在哪用inverse="ture"?      “set的inverse属性决定是否把对set的改动反映到数据库中去。inverse=false————反映;inverse=true————不反映”inverse属性默认为false      inverse属性默认是false的,就是说关系的两端都来维护关系。这个意思就是说,如有一个Student,   Teacher和TeacherStudent表,Student和Teacher是多对多对多关系,这个关系由TeacherStudent这个表来表 现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢?在用hibernate时,我们不会显示的对 TeacherStudent表做操作。对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指 定的是"谁"维护关系,那个在插入或删除"谁"时,就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了,就是说关系另一头的对象已经set 或是add到"谁"这个对象里来了。前面说过inverse默认是false,就是关系的两端都维护关系,对其中任一个操作都会处发对表系表的操作。当在 关系的一头,如Student中的bag或set中用了inverse="true"时,那就代表关系是由另一关维护的(Teacher)。就是说当这插 入Student时,不会操作TeacherStudent表,即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的 操作。所以,当关系的两头都用inverse="true"是不对的,就会导致任何操作都不处发对关系表的操作。当两端都是inverse= "false"或是default值是,在代码对关系显示的维护也是不对的,会导致在关系表中插入两次关系。         在一对多关系中inverse就更有意义了。在多对多中,在哪端inverse="true"效果差不多(在效率上)。但是在一对多中,如果要一方维护关 系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。而如果让"多"方面维护关系时就不会有update 操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管 怎样说,还是让"多"方维护关系更直观一些。    (1)对one-to-many而言,改变set,会让hibernate执行一系列的update语句, 不会delete/insert数据    (2)对many-to-many而言,改变set,只修改关系表的数据,不会影响many-to-many的另一方。    (3)虽然one-to-many和many-to-many的数据库操作不一样,但目的都是一个:维护数据的一致性。         3、cascade和inverse有什么区别?     可以这样理解,cascade定义的是关系两端对象到对象的级联关系;而inverse定义的是关系和对象的级联关系。  inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。cascade对关系标记都有效。  inverse对集合对象整体起作用,cascade对集合对象中的一个一个元素起作用,如果集合为空,那么cascade不会引发关联操作。  比如将集合对象置为null, school.setStudentSet(null)   inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?   cascade则不会执行对STUDENT表的关联更新, 因为集合中没有元素。  再比新增一个school, session.save(school)   inverse导致hibernate执行:    for( 对(school的每一个student ){     udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id    }   cascade导致hibernate执行:    for( 对school的每一个student ){     session.save(aStudent); //对学生执行save操作    }   extends:如果改变集合中的部分元素(比如新增一个元素),   inverse: hibernate先判断哪些元素改变了,对改变的元素执行相应的sql   cascade: 它总是对集合中的每个元素执行关联操作。    (在关联操作中,hibernate会判断操作的对象是否改变)  两个起作用的时机不同:   cascade:在对主控方操作时,级联发生。   inverse: 在flush时(commit会自动执行flush),对session中的所有set,hibernate判断每个set是否有变化,   对有变化的set执行相应的sql,执行之前,会有个判断:if( inverse == true ) return;可以看出cascade在先,inverse在后。   inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。      对one-to-many,hibernate对many方的数据库表执行update语句。      对many-to-many, hibernate对关系表执行insert/update/delte语句,注意不是对many方的数据库表而是关系表。    cascase 对set都是一致的,不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many方的数据库表。  4、cascade和inverse有什么相同?  这两个属性本身互不影响,但起的作用有些类似,都能引发对关系表的更新。  5、 建议:只对set + many-to-many设置inverse=false,其他的标记不考虑inverse属性,都设为inverse=true。对cascade,一 般对many-to-one,many-to-many,constrained=true的one-to-one 不设置级联删除。 

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


回复:inverse=true
文章收藏,  网上资源

tone发表评论于2007/4/1 18:47:38

http://tonyyl.javaeye.com/post/243217 看了很多关于inverse的文章,理解得也不是很透彻,这次做了这个项目,遇到这方面问题,就总结了一样关于inverse的配置, 1.关系parent和children的例子也已经说了怎么去配置一对多的关系,这个例子已经说明了在set端设置inverse=true,原因很简单,child长大了,不需要什么事情都要parent带到 其实在我看来,inverse=true实际上是去解放了one那一方,在这儿就是指parent对象,他不需要在每次更新的时候,都把children都加载出来。像在下面的代码中,不会有什么问题,应该都在一个session中完成的  ParentManager manager=(ParentManager) getBean("parentManager");          Parent parent=manager.getParent("pppp");          parent.setName("bob");manager.save(parent); 所以不会觉得有什么问题。但是在WEB应用的三层结构中,通常要把模型传递到表现层中,完成更新操作,那这个时候inverse=true的优势就明显了,他可以不用考虑Children就完成自身的更新 2.当inverse=false会发生什么? 那天也是无意注意到这个问题,突然把系统的所有inverse都设置成了false 当在插入一个新的对象的时候,不会出现什么问题。但是在更新父对象的时候,却发生了我不想看到的,Customer和Order是一对多的关系 Hibernate: update customer.name=?..........where customerName=? and version=? Hibernate: update orders set customer=null where customer=? 在更新了customer信息后,同时把订单中该用户的用户置为null,换句话说就是断了两者之间的联系。再看看代码   CustomerForm customerForm = (CustomerForm) form;  Customer customer = new Customer();  convertObject(customerForm, customer);  CustomerManager manager = (CustomerManager) getBean("customerManager");                                                                              manage.save(customer); customerForm是通过struts的form理到来自表单的数据。最后通过业务层的代码调用Hibernate的saveOrUpdate()方法进行保存。这个时候我发现我并未去加载和customer关系的order, 然后我修改了代码,试着把所有的该customer的order都提出来,并在保存前调用了 customer .setOrder(order); 结果发生异常 org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [com.us.ebuy.model.Customer#123]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.us.ebuy.model.Customer#123]org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.us.ebuy.model.Customer#123] at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:629) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:258) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:216) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70) at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:531) at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:523) at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:519) at org.springframework.orm.hibernate3.HibernateTemplate$18.doInHibernate(HibernateTemplate.java:690) at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:365) at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:687) at com.us.ebuy.dao.hibernate.BaseDAOHibernate.saveObject(BaseDAOHibernate.java:55) at com.us.ebuy.users.dao.hibernate.CustomerDAOHibernate.saveCustomer(CustomerDAOHibernate.java:64) at com.us.ebuy.users.service.impl.CustomerManagerImpl.saveCustomer(CustomerManagerImpl.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:335) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:181) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:148) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176) at $Proxy2.saveCustomer(Unknown Source) at com.us.ebuy.users.action.CustomerAction.save(CustomerAction.java:133) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:274) at org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:194) at com.us.ebuy.action.BaseAction.execute(BaseAction.java:143) at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:419) at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:224) at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196) at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) at javax.servlet.http.HttpServlet.service(HttpServlet.java:709) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:174) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:75) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) at java.lang.Thread.run(Thread.java:595) 可以看出,在更新的时候,他执行了插入操作,才会认为有重复的key,后来我想到了使用hibernate的merge方法,通过合作,才能完成customer的更新操作,但这这种更新方法显然是我们不愿意看到的。后来通过检测配置文件才发现,set 元素的inverse=false,最后我想是这儿的原因,改为false后,还是通过saveOrUpdate()方法更新对象,问题解决了。 通过上面的问题,可以更进一步的清楚了,Hibernate在对对待持久化的对象和游离状态的对象是不一样的,上述问题通常都是发生在处理游离状态的对象时候,而在三层结构中,我们会经常处理这种问题的,看来是要重新理解对象的状态问题。  

个人主页 | 引用回复 | 主人回复 | 返回 | 编辑 | 删除
 


» 1 »

发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.063 second(s), page refreshed 144761792 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号