在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
解决方法:设置lazy为false
(参考资料: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了,能保存了。