Hibernate 之 cascade 和 inverse

1、Inverse在一对多关系和多对多关系中被声明使用(在多对一中没有inverse关键字) 理解关键字:关系

"inverse",应该改成“relationship owner"吗?
在hibernate,只有“关系的拥有者”才能维护两个实体类之间的关联关系(一对多或多对多)。“inverse”关键字创建的目的是指明哪一边(实体类)是关系的拥有者。 然而“inverse”关键字生涩难懂,建议改为“relationship_owner”
总之,inverse=“true”说明当前配置文件对应的实体类不是关系的拥有者,而其对应的关联实体类是关系的拥有者(也就是inverse属性所在元素对应的集合变量引用的实体类为关系的拥有者),反之相反。

2、cascade属性的作用是描述关联对象进行操作时的级联特性。因此,只有涉及到关系的元素才有cascade属性。 理解关键字:级联

3、上示例代码:

背景描述:城市、学校、学生三个对象;全部是1对多的关系;即

TestCity.java

package com.test;

import java.util.HashSet;
import java.util.Set;

/**
 * 测试-城市
 * Created by zmf on 16/4/13.
 */
public class TestCity {
    private int id;

    private String name;

    private Set<TestSchool> schoolSet = new HashSet<TestSchool>();

    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<TestSchool> getSchoolSet() {
        return schoolSet;
    }

    public void setSchoolSet(Set<TestSchool> schoolSet) {
        this.schoolSet = schoolSet;
    }
}

TestSchool.java

package com.test;

import java.util.HashSet;
import java.util.Set;

/**
 * 测试-学校
 * Created by zmf on 16/4/13.
 */
public class TestSchool {
    private int id;

    private String name;

    private Set<TestStudent> students = new HashSet<TestStudent>();

    private TestCity city;

    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<TestStudent> getStudents() {
        return students;
    }

    public void setStudents(Set<TestStudent> students) {
        this.students = students;
    }

    public TestCity getCity() {
        return city;
    }

    public void setCity(TestCity city) {
        this.city = city;
    }
}

TestStudent.java

package com.test;

import java.util.HashSet;

/**
 * 测试-学生
 * Created by zmf on 16/4/13.
 */
public class TestStudent {
    private int id;

    private String name;

    private TestSchool school;

    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 TestSchool getSchool() {
        return school;
    }

    public void setSchool(TestSchool school) {
        this.school = school;
    }
}

映射文件:

TestCity.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
    <class name="com.test.TestCity">
		<id name="id">
            <generator class="identity" />
        </id>
		
		<property name="name"/>

		<set name="schoolSet">
			<key column="city_id"></key>
			<one-to-many class="com.test.TestSchool"></one-to-many>
		</set>

    </class>
</hibernate-mapping>

TestSchool.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
    <class name="com.test.TestSchool">
		<id name="id">
            <generator class="identity" />
        </id>
		
		<property name="name"/>

		<many-to-one name="city" column="city_id" class="com.test.TestCity"></many-to-one>

		<set name="students">
			<key column="school_id"></key>
			<one-to-many class="com.test.TestStudent"></one-to-many>
		</set>

    </class>
</hibernate-mapping>

TestStudent.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
    <class name="com.test.TestStudent">
		<id name="id">
            <generator class="identity" />
        </id>
		
		<property name="name"/>

		<many-to-one name="school" column="school_id" class="com.test.TestSchool"></many-to-one>

    </class>
</hibernate-mapping>

测试代码

package com.test;

import com.util.HibernateSessionFactory;

public class TestInverseAndCascade {
	public static void main(String[] args) {
		//testInverse_false();

		//两种删除方式
		TestCity city = (TestCity) HibernateSessionFactory.get(TestCity.class, 21);
//		TestCity city = new TestCity();
//		city.setId(19);
//		//这时,city中的集合为空,则不会删除集合中的对象;
		HibernateSessionFactory.delete(city);
	}

	/**
	 * 测试inverse属性为false时的关系维护,以及SQL的执行; 默认为false.
	 */
	public static void testInverse_false () {
		TestCity city = new TestCity();
		city.setName("北京");

		TestSchool school = new TestSchool();
		school.setName("北京一中");

		TestStudent student1 = new TestStudent();
		student1.setName("张三");

		TestStudent student2 = new TestStudent();
		student2.setName("李四");

		//在主控方来设置关联关系
		//城市和学校
		city.getSchoolSet().add(school);
		//学校和学生
		school.getStudents().add(student1);
		school.getStudents().add(student2);

		//只处理城市
		HibernateSessionFactory.add(city);

		/*
		1 / 没有设置cascade时,sql如下,且报错
		Hibernate: insert into TestCity (name) values (?)
		Hibernate: update TestSchool set city_id=? where id=?

		由此可见,inverse只是负责维护之间的关系,并不负责处理save/update/delete关联对象(由cascade控制)

		此处有2个解决办法,第一个是在主控方来设置cascade;第二种是自己保存城市和学生,然后由inverse来控制关系
		第一种办法,先设置cascade为all,所有情况下均执行;

		cascade属性可选值:
		all : 所有情况下均进行关联操作。
		none:所有情况下均不进行关联操作。(默认值)
		save-update:在执行save/update/saveOrUpdate时进行关联操作。
		delete:在执行delete时进行关联操作。

		设置好,执行;

		Hibernate: insert into TestCity (name) values (?)
		Hibernate: insert into TestSchool (name, city_id) values (?, ?)
		Hibernate: update TestSchool set city_id=? where id=?
		Hibernate: update TestStudent set school_id=? where id=? //学校的学生set节点也要加
		设置好,执行:
		Hibernate: insert into TestCity (name) values (?)
		Hibernate: insert into TestSchool (name, city_id) values (?, ?)
		Hibernate: insert into TestStudent (name, school_id) values (?, ?)
		Hibernate: insert into TestStudent (name, school_id) values (?, ?)
		Hibernate: update TestSchool set city_id=? where id=?
		Hibernate: update TestStudent set school_id=? where id=?
		Hibernate: update TestStudent set school_id=? where id=?

		**cascade级联先发生,最后hibernate会根据inverse来判断是否来控制关联关系!
		**cascade用于触发增删改的级联操作,inverse来控制关联关系;

		设计时,要根据实际情况:
		1/城市的建立,不一定要建学校;
		2/学校建的时候会选择在哪个城市来建校,所以关系应该由学校控制;所以城市的set集合inverse应该设置为true;
		3/城市更名时,和学校的属性没有多大关系,房间和学生也不会更改任何属性.
		4/一旦城市被炸毁,则学校也一同死掉,但学生很可能因为事先逃亡了,所以没有死,只是不上学了.因此
		城市中的set节点应该是

		<set name="schoolSet" cascade="delete" inverse="true">
			<key column="city_id"></key>
			<one-to-many class="com.test.TestSchool"></one-to-many>
		</set>

		学校中,多对一的城市节点,应该是 cascade=none, 即学校改名/被拆,和城市没有关系,不用级联操作;
		<many-to-one name="city" column="city_id" cascade="none" class="com.test.TestCity" lazy="false"></many-to-one>

		学校中的学生,同理,建校时,没有学生,学校被拆和学生没有关系;学生上学和学校有关系,学生改名或者死掉不会影响学校

		<set name="students" inverse="true" cascade="none">
			<key column="school_id"></key>
			<one-to-many class="com.test.TestStudent"></one-to-many>
		</set>

		学生中的学校
		<many-to-one name="school" column="school_id" class="com.test.TestSchool" cascade="none" lazy="false"></many-to-one>


		**所以inverse 90%以上是由many来控制的,也是默认的.在one的一方,set标签中设置inverse=true.
		cascade 90%不用设置是(默认值none).即一方改名和删除和另一方没有关系.如果像城市/学校这样,城市被毁,则学校被毁,就设置delete.
		同样要设置delete的有 订单-订单明细;  用户-消费记录/登录记录;

		以上的代码要改成

		TestCity city = new TestCity();
		city.setName("北京");
		//创建城市,和玩游戏一样
		HibernateSessionFactory.add(city);

		//创建学校
		TestSchool school = new TestSchool();
		school.setName("北京一中");
		//设置关系
		school.setCity(city);
		HibernateSessionFactory.add(school);

		//学生出生
		TestStudent student1 = new TestStudent();
		student1.setName("张三");
		HibernateSessionFactory.add(student1);

		TestStudent student2 = new TestStudent();
		student2.setName("李四");
		HibernateSessionFactory.add(student2);

		//学生1去上学
		student1.setSchool(school);
		HibernateSessionFactory.update(student1);

		//学生2去上学
		student2.setSchool(school);
		HibernateSessionFactory.update(student2);

		Hibernate: insert into TestCity (name) values (?)
		Hibernate: insert into TestSchool (name, city_id) values (?, ?)
		Hibernate: insert into TestStudent (name, school_id) values (?, ?)
		Hibernate: insert into TestStudent (name, school_id) values (?, ?)
		Hibernate: update TestStudent set name=?, school_id=? where id=?
		Hibernate: update TestStudent set name=?, school_id=? where id=?


		考虑对象关系时,考虑2点:
		1/我的属性值改变或者我的存在消失,是否影响你的状态和你的消失,如果有影响则有cascade;
		2/我们的关系,是你管理好,还是我管理好.如果你关系好,则设置inverse=true;many一方没有这个属性;
		*/
	}


	/*
	sql脚本
	delete testSchool;
	
	DELETE  testCity;
	
	DELETE  testStudent;
	
	DECLARE @school_id int;
	INSERT INTO  testCity (name) VALUES ('北京');
	INSERT INTO  testSchool (name,city_id) VALUES ('北京一中', @@identity);
	SET @school_id=  @@identity;
	INSERT INTO  testStudent (name,school_id) VALUES ('张三', @school_id);
	INSERT INTO  testStudent (name,school_id) VALUES ('李四', @school_id);
	
	
	SELECT * from testCity
	SELECT * from testSchool
	SELECT * from testStudent
	 */
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值