Hibernate中inverse和cascade及相应异常处理

在hibernate中,inverse和cascade常常容易弄混淆,下面简单的说明一下二者区别。

cascade:
        这里咱们以annotation做为例子。
        cascade={CascadeType.ALL}

CascadeType是枚举类型,其取值能够是:
  • ALL    Cascade all operations全部状况
  • MERGE  Cascade merge operation合并(merge=save+update)
  • PERSIST  Cascade persist operation存储 persist()
  • REFRESH  Cascade refresh operation刷新
  • REMOVE   Cascade remove operation删除
            拿persist做为例子,当把一个对象存进数据库时,若是这个对象的cascade设为persist那么,与它相对应的那个对象能够自动被保存进数据库,而不须要显示操做。其余操做类同。PS:对于annotation来讲,cascade所在的类的行为是”主动级联“,cascade所标示的成员属性是”被动级联“。对于insert操做来讲,插入一个主动级联的对象,会自动将被动级联的对象插入。

inverse(至关于annotation中的mappedBy):
        inverse是比较容易迷糊的一个属性,这个属性出如今hbm.xml文件中,annotation没有。
        在many-to-many中:
        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值是,在代码对关系显示的维护也是不对的,会致使在关系表中插入两次关系。

        在one-to-many中
      在一对多关系中inverse就更有意义了。在多对多中,在哪端inverse="true"效果差很少(在效率上)。可是在一对多中,若是要一方维护关 系,就会使在插入或是删除"一"方时去update"多"方的每个与这个"一"的对象有关系的对象。而若是让"多"方面维护关系时就不会有update 操做,由于关系就是在多方的对象中的,直指插入或是删除多方对象就好了。固然这时也要遍历"多"方的每个对象显示的操做修关系的变化体现到DB中。无论 怎样说,仍是让"多"方维护关系更直观一些。
        PS:能够作一个简单的one-to-many实验,设“一”的一方cascade=“save-update”,inverse=“false”。这样对“一”进行操做就会级联操做“多”方,而且由“一”维护关系。插入数据时会发现,在“多”方有多余的update操做,这是“一”方维护关系的结果。
        inverse的实际效果就是减小没必要要的SQL语句


        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方的数据库表。


        cascade和inverse有什么相同?
        这两个属性自己互不影响,但起的做用有些相似,都能引起对关系表的更新。( 可是,teacher和student为例,若是只设置inverse而不设置cascade,存储teacher时并不会保存student,并且会报错!


        建议:只对set + many-to-many设置inverse=false,其余的标记不考虑inverse属性,都设为inverse=true。对cascade,一 般对many-to-one,many-to-many,constrained=true的one-to-one 不设置级联删除。
 
 
异常:

异常1:not-null property references a null or transient value
解决方法:将“一对多”关系中的“一”方,not-null设置为false
(参考资料:http://www.thearcmind.com/confluence/pages/viewpage.action?pageId=212)

异常2:org.hibernate.TransientObjectException: object references an unsaved transient instance
解决方法:cascade="save-update,persist"
(参考资料:http://www.laliluna.de/254.html)

异常3:org.hibernate.QueryException: could not resolve property
解决方法:"from Category category where category.userID = :userID"修改成"from Category category whereuserID = :userID"或者"from Category category where category.user.id = :userID"
(参考资料:http://www.laliluna.de/277.html)

异常4:could not initialize proxy - the owning Session was closed
解决方法:设置lazyfalse
(参考资料:http://forum.springframework.org/showthread.php?t=27993)
php

 

异常2我在应用中碰到了这样的问题: html

有三个表:userInfo   deptmentInfo   role spring

userInfo 与deptmentInfo为many to one sql

userInfo 与role为many to one 数据库

下面为保存UserInfo对象时的代码: session

    DeptmentInfo dept = new DeptmentInfo();
    dept.setDeptName(deptName);
app

    Role role = new Role();
    role.setRoleName(roleName);
dom

    UserInfo user = new UserInfo();
    user.setUserName(userName);
    user.setUserSex(userSex);
    user.setDuty(duty);
    user.setPhone(phone);
    user.setMobileNum(mobileNum);
    user.setEmail(email);
    user.setQq(qq);
    user.setMsn(msn);
    user.setAdress(adress);
    user.setDeptmentInfo(dept);
    user.setRole(role);
fetch

    dao.save(user); spa

 

执行时有错:org.hibernate.TransientObjectException: object references an unsaved transient instance

 

因而改了UserInfo.hbm.xml的一些地方

以下:

<many-to-one name="role" class="com.oa.domain.Role"
   cascade="save-update,persist" fetch="select">
    <column name="ROLE_ID" precision="22" scale="0" />
   </many-to-one>
   <many-to-one name="deptmentInfo"
    class="com.oa.domain.DeptmentInfo" cascade="save-update,persist"
    fetch="select">
    <column name="DEPT_ID" precision="22" scale="0" />
   </many-to-one>

 

加了上面红色部分的,就OK了,能保存了。

相关文章
相关标签/搜索