您的位置:首页 > 其它

Hibernate中的inverse和cascade属性

2014-11-07 19:48 393 查看
Hibernate中的inverse在表关系映射中经常应用,

  inverse的值有两种,“true”和“false”。inverse="false"是默认的值,如果设置为true 则表示对象的状态变化不会同步到数据库 ;设置成false则相反;

  inverse的作用:在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的。

  我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBC exception的throw。这是我们在建立实体类关系时必须需要关注的地方。一般来说,inverse=true是推荐使用,双向关联中双方都设置 inverse=false的话,必会导致双方都重复更新同一个关系。但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是
不行的,好在一对多中的一端:many-to-one默认是inverse=false,避免了这种错误的产生。但是多对多就没有这个默认设置了,所以很 多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。所以说,双向关联中最 好的设置是一端为inverse=true,一端为inverse=false。一般inverse=false会放在多的一端,那么有人提问了, many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。

  cascade 有五个选项 分别是: all ,delete ,none,save-update,delete-orphan ;

  all : 所有情况下均进行关联操作。

  none:所有情况下均不进行关联操作。这是默认值。

  save-update:在执行save/update/saveOrUpdate时进行关联操作。

  delete:在执行delete时进行关联操作。

  delete-orphan: 当save/update/saveOrUpdate时,相当于save-update ;当删除操作时,相当于delete ;

  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 不设置级联删除。

文章2

在hibernate中一对多关联时会经常用到inverse和cascade属性 ,

inverse 有两个值 true ,false ;如果设置为true 则表示对象的状态变化不会同步到数据库 ;设置false就相反拉;

cascade 有五个选项 分别是: all ,delete ,none,save-update,delete-orphan ;

all : 所有情况下均进行关联操作。

none:所有情况下均不进行关联操作。这是默认值。

save-update:在执行save/update/saveOrUpdate时进行关联操作。

delete:在执行delete时进行关联操作。

delete-orphan: 当save/update/saveOrUpdate时,相当于save-update ;当删除操作时,相当于delete ;

all的意思是save-update + delete

all-delete-orphan 的意思是当对象图中产生孤儿节点时,在数据库中删除该节点

all比较好理解,举个例子说一下all-delete-orphan:

Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items.

举个例子,现items中存两个Item, item1,item2,如果定义关系为all-delete-orphan

当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例

将变成孤儿节点,当执行category.update(),或session.flush()时

hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉

测试Hibernate中的三个属性:lazy,inverse,cascade

【测试环境】

一对多关系的两张表:boy、girl(一个男孩可以多个女朋友)

boy表结构

Field Type

------ -----------

name varchar(50) pk

age varchar(50)

girl表结构

Field Type

------ -----------

name varchar(50) pk

bf varchar(50) fk

ER图





【保存时:Inverse与cascade】

创建三个girl对象和一个boy对象,让这是三个girl都是boy的女朋友

---------创建对象的代码片段-----------

Boy boy = new Boy("tom","23", null);

Set girls = new HashSet();

Girl g[] = new Girl[]{

new Girl("Alice1", boy),

new Girl("Alice2", boy),

new Girl("Alice3", boy)};

girls.add(g[0]);

girls.add(g[1]);

girls.add(g[2]);

boy.setGirls(girls);

在Boy.hbm.xml中设置,然后对boy对象进行保存。

1.Inverse = true,不指定cascade

cascade的默认值为none, 当对boy进行保存操作时,girl什么都不做. 所以只保存了boy对象, 没有保存girl对象

2.Inverse = true,cascade=all

boy与girl对象,包扩外键都成功保存。

(生成3条SELECT语句和4条INSERT语句,一下简称SELECT 3, INSERT 4)

3.Inverse = false,不指定cascade

报错。因为boy为主控方,负责维护关系,所以在插入boy对象后,会尝试修改并不存在的girl对象。

4.Inverse = false,cascade=all

boy与girl对象,包扩外键都成功保存。

(SELECT 4, INSERT 4, UPDATE 3)

分析:除了4条INSERT语句之外,其他的6条语句是我们为了图方便付出的代价:3条SELECT语句用来判断girl对象是否在数据表中已经存在,3条UPDATE语句是为了维护外键关系

高效率的做法:在Boy.hbm.xml中设置Inverse=true,在Girl.hbm.xml中设置Inverse=false, cascade=all,然后保存三个girl对象

(SELECT 1, INSERT 4)

高效率的代价就是保存的时候比较麻烦

【删除时:Inverse与cascade】

希望通过删除boy,也将3个girl对象删除。程序中先查出boy对象,然后进行删除

-----------------------------------------

Boy boy = (Boy) s.get(Boy.class, "tom");

s.delete(boy);

-----------------------------------------

同样在Boy.hbm.xml中进行设置

1.Inverse = true

可以猜到结果是出错。原因:外键约束错误

2.Inverse = false

boy删除,girl表中外键变为null,没有删除记录 ;

(UPDATE 1, DELETE 1)

3.Inverse = false, cascade = all

全部删除 ;在删除有外键的从表时,先把从表外键置为null,然后删除主表记录,最后根据从表主键删除所有相关从表记录

(UPDATE 1, DELETE 4)

4.Inverse = true, cascade = all

全部删除

(DELETE 4)

Inverse是hibernate双向关系中的基本概念,当然对于多数实体,我们并不需要双向关联,更多的可能会选择单向关联,况且我们大多数人一般采用一对多关系,而一对多双向关联的另一端:多对一的inverse属性是不存在,其实它默认就是inverse=false.从而防止了在一对多端胡乱设置inverse也不至于出错。但是inverse设置不当确实会带来很大的性能影响,这点是我们必须关注的。

这篇文章已经详细分析了inverse设置不当带来的影响:

http://www.hibernate.org/155.html

看了这篇文章,还是很有必要再写下一些总结的:

1)inverse中提及的side其实是指一个类或者表的概念,双向关联其实是指双方都可以取得对方的应用。

2)维护关系这个名词还是稍显模糊或者晦涩。我们一般说A类或者A表(这里的表的是指多对多的连接表)有责任维护关系,其实这里的意思是说,我在应用在更新,创建,删除(读就不用说了,双向引用正是为了方便读而出现)A类或者A表时,此时创建的SQL语句必须有责任保证关系的正确修改。

3)inverse=false的side(side其实是指inverse=false所位于的class元素)端有责任维护关系,而inverse=true端无须维护这些关系。

4)我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBC exception的throw。这是我们在建立实体类关系时必须需要关注的地方。一般来说,inverse=true是推荐使用,双向关联中双方都设置inverse=false的话,必会导致双方都重复更新同一个关系。但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是不行的,好在一对多中的一端:many-to-one默认是inverse=false,避免了这种错误的产生。但是对多对就没有这个默认设置了,所以很多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。所以说,双向关联中最好的设置是一端为inverse=true,一端为inverse=false。一般inverse=false会放在多的一端,那么有人提问了,many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。

看下面的多对多的定义大家更会清楚”多对多“与“一对多”的关系:其中我们注意<many-to-many />标签的特点就知道,它是定义了一个多对多关系,而不是<one-to-many/>。

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping package="org.hibernate.auction">

<class name="TestA" table="TestA"

dynamic-update="true" dynamic-insert="true" >

<id name="id" column="id" type="int" unsaved-value="any" >

<generator class="assigned">

</generator>

</id>

<property name="name" type="java.lang.String"

update="true" insert="true" column="name" />

<set name="testBs" table="TestA_TestB" inverse="false" cascade="all">

<key column="testA"/>

<many-to-many column="testB" class="TestB" />

</set>

</class>

<class name="TestB" table="TestB"

dynamic-update="true" dynamic-insert="true" >

<id name="id" column="id" type="int" unsaved-value="any" >

<generator class="assigned">

</generator>

</id>

<property name="name" type="java.lang.String" update="true"

insert="true" column="name" />

<set name="testAs" table="TestA_TestB" inverse="true" cascade="all">

<key column="testB"/>

<many-to-many column="testA" class="TestA" />

</set>

</class>

</hibernate-mapping>

在对多对中,因为一端维护关系另一端不维护关系的原因,我们必须注意避免在应用中用不维护关系的类建立关系,因为这样建立的关系是不会在数据库中存储的。基于上面的映射文件代码给出一个例子:

package org.hibernate.auction;

import java.util.*;

/**

* @author Administrator

*

* To change the template for this generated type comment go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

public class TestA {

int id;

String name;

Set testBs=new HashSet();

public TestA(){

}

public TestA(int id){

setId(id);

}

public int getId(){

return id;

}

public void setId(int id){

this.id=id;

}

public String getName(){

return name;

}

public void setName(String name){

this.name=name;

}

public Set getTestBs(){

return testBs;

}

public void setTestBs(Set s){

testBs=s;

}

public void addTestB(TestB tb){

testBs.add(tb);

}

public static void main(String[] args) {

}

}

public class TestB {

int id;

String name;

Set testAs=new HashSet();

public TestB(){

}

public TestB(int id){

setId(id);

}

public int getId(){

return id;

}

public void setId(int id){

this.id=id;

}

public String getName(){

return name;

}

public void setName(String name){

this.name=name;

}

public Set getTestAs(){

return testAs;

}

public void setTestAs(Set s){

testAs=s;

}

public void addTestA(TestA ta){

testAs.add(ta);

}

public static void main(String[] args) {

}

}

测试代码:

public void doTest() throws Exception{

TestA a1=new TestA(1);

TestA a2=new TestA(2);

TestA a3=new TestA(3);

TestB b1=new TestB(1);

TestB b2=new TestB(2);

TestB b3=new TestB(3);

a1.addTestB(b1);

a1.addTestB(b2);

a1.addTestB(b3);

b2.addTestA(a1);

b2.addTestA(a2);

Session s = factory.openSession();

s = factory.openSession();

Session session = factory.openSession();

session.save(a1);

session.flush();

session.close();

}

测试后连接表的数据为:

testa testb

1 1

1 2

1 3

根据inverse规则,对这些代码:b2.addTestA(a1); b2.addTestA(a2); 建立的关系,数据库并没有存储下来,因为TestB没有责任维护这些关系,所以产生的sql语句自然不会有针对Testa_testB表的操作了。假设应用中真的需要这些方法,那么我们可以修改TestB的方法,让他们注意在维护端类中执行相应的操作以使得关系能够在数据库中保存下来,更改TestB如下:

/*

* Created on 2004-7-25

*

* To change the template for this generated file go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

package org.hibernate.auction;

import java.util.*;

/**

* @author Administrator

*

* To change the template for this generated type comment go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

public class TestB {

int id;

String name;

Set testAs=new HashSet();

public TestB(){

}

public TestB(int id){

setId(id);

}

public int getId(){

return id;

}

public void setId(int id){

this.id=id;

}

public String getName(){

return name;

}

public void setName(String name){

this.name=name;

}

public Set getTestAs(){

return testAs;

}

public void setTestAs(Set s){

testAs=s;

}

public void addTestA(TestA ta){

testAs.add(ta);

ta.addTestB(this);

}

public static void main(String[] args) {

}

}

那么测试执行后连接表的数据为:

testa testb

1 2

1 3

1 1

2 2

测试通过。

文章3

Hibernate在映射一对多关系时,有两种方式,一种是单向一对多,一种是双向关系。两者相比,双向一对多的好处体现在两方面:

首先,也是很明显的一点,由于是双向关联,我们在实际业务逻辑时将更方便,例如我们可以检索一个Category下的所有Product,同时还可以检索出Product属于哪个。

其次,双向关系相对单向关系而言,在数据库的访问方面更有优势。双向关联比单向关联唯一的”劣势“,就在于双向关联需要比单向关联多写一个映射文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: