NHibernate经验之谈:Inverse与Cascade

inverse 与Cascade分别表示NHibernate中类之间关系、级联操作。他们之间不同值以及不一样的组合,很多时候都影响着业务逻辑的执行。其实inverse与Cascade又是不一样的。

inverse:意思是反转,它指明类之间的关系由谁来进行维护。例如:班级(Class类)与学生(Student),班级-学生就是one-to-many,学生-班级就是many-to-one。

它只能在one-to-many 中many的一方进行设置(当然,在many一方,可能是在<set>也可能是在<bag>也可能是在<array>中进行设置),而对于one方,是不进行Inverse设置的。在Hibernate社区,inverse默认值为false。

Cascade:则表示一种级联操作,它的值有none\save-update\delete\all\all-delete-ophan,它主要作用是在父类子类之间进行操作时,哪些操作会对父类(如班级Class类)

、子类(Student)有影响。在Hibernate社区,Cascade默认值为none。

下面通过一个示例说说他们在实际应用中应该如何进行设置。

还是以班级(Class类)与学生(Student)来进行说明。

Student实体类定义:
public class Student
	{
		public virtual int Id { get; set; }


		public virtual string Name { get; set; }


		public virtual int Gender { get; set; }


		public virtual Class Class { get; set; }


		//public virtual int ClassId { get; set; }


		public virtual IList<Course> Courses { get; set; }
	}


配置(基本部分,不含many-to-one配置。因为各种测试下inverse\cascade值可能不同,所以具体将在各种测试中给出):
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
	<class name="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse" lazy="true" table="Student">
		<id column="Id" name="Id" type="Int32">
			<generator class="native"></generator>
		</id>
		<property name="Name" column="Name"></property>
		<property name="Gender" column="Gender"></property>
		<!--<property name="ClassId" column="ClassId"></property>-->
	</class>
</hibernate-mapping>




Class实体类定义
	public class Class
	{
		public virtual int Id { get; set; }


		public virtual string SerialNO { get; set; }


		public virtual int DepartmentId { get; set; }


		public virtual ISet<Student> Students { get; set; }
	}




配置:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
	<class name="NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse">
		<id name="Id" >
			<generator class="native"></generator>			
		</id>
		<property column="SerialNO" name="SerialNO" type="string"></property>
		<property column="DepartmentId" name="DepartmentId" type="Int32"></property>	
	</class>
</hibernate-mapping>


测试1:希望在添加班级时,将若干新学生到该班级中。(注意:在此次测试中,删除了Class表与Student表之间的主外键关系。正常为保证数据的完整性还是应建立主外键关系)


Student中many-to-one 配置:
		<many-to-one   name="Class" 
			 class="NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse" >
			<column name="ClassId" not-null="false" sql-type="int"></column>			
		</many-to-one>




Class 中one-to-many配置

		<set name="Students" lazy="false"  cascade="all" inverse="false">
			<key column="ClassId" ></key>
			<one-to-many class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"/>
		</set>



此时,维护Class与Student直接关系的是Class
测试代码:
	[Test]
		public void ClassAddTest()
		{
			Class classEntity = new Class
			                    	{
			                    		SerialNO = "C0302",
			                    		DepartmentId = 36,
			                    		Students =
			                    			new HashedSet<Student> {new Student {Name = "zhangsan"}, new Student {Name = "zhangsan1"}}
			                    	};
			var repository = new ClassRepository();
			repository.Add(classEntity);
		}


测试结果:


由测试结果可知:Class与Student对象都保存至数据库,而Student对象是先将外键字段设置为null,然后根据插入的Class对象的ClassId
将刚插入的Student进行批量更新。


测试2:将Class中many设置为:
		<set name="Students" lazy="false"  cascade="all" inverse="true">
			<key column="ClassId" ></key>
			<one-to-many class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"/>
		</set>


此时,维护Class与Student直接关系的是Student
Student中Class设置为:
		<many-to-one   name="Class" 
					 class="NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse" >
			<column name="ClassId" not-null="false" sql-type="int"></column>			
		</many-to-one>


测试结果:


通过以上信息可知:数据被正常保存了,插入数据时,已经为待插入的Student对象构造好了外键。
所以在one-to-many关联中一般通过将many一方(如上例中的Student)作为维护双方关系的主控方,也就是在one(如上例中的Class)设置inverse="true"


添加一个many-to-many的映射,来测试一下 Inverse对此关系的影响。新增一个Course类(课程)。一个学生对应多门课程,一门课程也对应许多学生。
	public class Course
	{
		public virtual int Id { get; set; }


		public virtual string Name { get; set; }


		public virtual int TeacherId { get; set; }


		public virtual IList<Student> Students { get; set; }
	}


Course的映射:(many-to-many部分没有给出,在各种测试中进行不同配置)
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
	<class name="NHibernateCascadeInverse.Entities.Course,NHibernateCascadeInverse" lazy="true" >
		<id column="Id" name="Id" type="Int32">
			<generator class="native"></generator>
		</id>
		<property name="Name" column="Name"></property>
		<property name="TeacherId" column="teacherID"></property>
	</class>
</hibernate-mapping>




Student的映射:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
	<class name="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse" lazy="true" table="Student">
		<id column="Id" name="Id" type="Int32">
			<generator class="native"></generator>
		</id>
		<property name="Name" column="Name"></property>
		<property name="Gender" column="Gender"></property>
		<many-to-one   name="Class" 
					 class="NHibernateCascadeInverse.Entities.Class,NHibernateCascadeInverse" >
			<column name="ClassId" not-null="false" sql-type="int"></column>			
		</many-to-one>
		<property name="ClassId" column="ClassId"></property>
	</class>
</hibernate-mapping>




测试三:添加学生和课程信息(学生与课程之间有个中间表CourseInfo保存学生选课信息):
设置Student为主控方,即Course的Inverse设置为true.
Student many-to-many映射
	<bag name="Courses" inverse="false"  table="CourseInfo" cascade="all">
			<key foreign-key="FK_CourseInfo_Student" column="StudentId"></key>
			<many-to-many class="NHibernateCascadeInverse.Entities.Course,NHibernateCascadeInverse" column="CourseId"></many-to-many>
		</bag>


Course many-to-many映射
	<bag inverse="true" name="Students" table="CourseInfo" cascade="all">
			<key foreign-key="FK_CourseInfo_Course" column="CourseId">				
			</key>
			<many-to-many column="StudentID" class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"></many-to-many>
		</bag>




测试代码:
		[Test]
		public void ManyToMany_Test()
		{
			var student = new Student { Gender = 1, Name = "testAccount", ClassId = 10 };
			var course = new Course { Name = "Computer", TeacherId = 1 };
			student.Courses = new List<Course> { course };
			course.Students = new List<Student> { student };
			StudentRepository studentRepository = new StudentRepository();
			CourseRepository courseRepository = new CourseRepository();
			//courseRepository.Add(course);
			studentRepository.Add(student);			
		}


测试结果:




测试三中,以Student为主控方,保存信息的时候也以Student进行保存。倘若以Course一方进行保存,数据也能正常写入吗?


测试四:

	[Test]
		public void ManyToMany_Test()
		{
			var student = new Student { Gender = 1, Name = "testAccount", ClassId = 10 };
			var course = new Course { Name = "Computer", TeacherId = 1 };
			student.Courses = new List<Course> { course };
			course.Students = new List<Student> { student };
			CourseRepository courseRepository = new CourseRepository();
			courseRepository.Add(course);			
		}


测试结果:


有测试三、测试四可以看出:两种情况下就Student与Course表的写入顺序不一样,但都能保存正常。
所以在many-to-many中,以哪方作为主控方,没有影响。


Cascade:设置对象关联关系中的级联操作。以上用了cascade="all"。其实它还有其他值:
cascade 可选值:none、save-update、delete、all-delete-orphan。

先通过一个测试简单了解cascade 的作用。


测试五:在测试1 中,
将many-to-one的配置改为如下:
<set name="Students" lazy="false"   inverse="true">
			<key column="ClassId" ></key>
			<one-to-many class="NHibernateCascadeInverse.Entities.Student,NHibernateCascadeInverse"/>
</set>


Student的配置还如测试一,保持不变。
测试结果如下:


由此可见,不设置cascade情况下,只保存了Class数据。


save-update、delete这几个都好理解。那all与all-delete-orphan有什么区别呢?


测试六:级联关系在Class中设置Cascade为all.
还以Class与Student为例进行删除测试。
测试准备:通过测试1,添加一个班级、两条学生信息。
测试:将删除班级中一条学生信息,然后删除班级
测试代码:
		[Test]
		public  void DeleteClassTest()
		{
			var repository = new ClassRepository();
			Class @class = repository.Get(287);
			IEnumerator<Student> students = @class.Students.GetEnumerator();
			Student student = null;
			if (students.MoveNext())
			{
				student = students.Current;
			}
			if (null!=student)
			{
				@class.Students.Remove(student);
			}
			repository.Delete(@class);
		}


测试七:级联关系在Class中设置Cascade为all-delete-orphan.
测试代码同测试六
测试结果:


由测试六、测试七可知:对于删除与父类之间的关系时,all-delete-orphan会将孤儿删除(orphan意为:孤儿),而all不会删除,而是将与父关联的外键字段设置为null。

代码下载: http://download.csdn.net/detail/tyb1222/4390075
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值