NHibernate从入门到精通系列(9)——一对多关联映射
2015-09-15 16:57
477 查看
内容摘要
单向关联映射
双向关联映射
一、单向关联映射
1.1 单向关联映射的描述
让我们回顾一下之前讲的《多对一关联映射》,如图1.1.1所示,其实“一对多”关联映射就是“多对一”关联映射相反的映射。
图1.1.1
至于“一对多”单向关联映射的代码如下:
映射文件如下:
我们看到“Class”类中,有名为“Students” 的属性,其类型是IList<Student>。在映射文件中,我们使用<bag>和<one-to-many>标签来描述“一对多”关联映射。
1.2 单向关联映射的数据插入
单元测试类的代码如下:
我们配置log4net输出SQL语句:
运行效果如图1.2.1所示,先成“insert into”语句,然后生成“update”语句修改外键。
图1.2.1
从图1.2.1中,我们能够观察到,如果“一对多”的外键不允许空,就有可能插入不成功。
我们将映射文件稍作修改:
运行效果如图1.2.2所示,抛出“ClassID”不允许插入NULL的异常。
图1.2.2
二、双向关联映射
2.1 双向关联映射的描述
我们修改一下代码,来实现“一对多”双向关联映射:
“一对多”双向关联映射指的是:在“一”的这端(“Class”类这端)包含“多”的属性(“Students”);在“多”的这端包含“一”的属性(“Class”属性)。这样两个类构成的循环引用就是双向关联映射。
2.2 双向关联映射的数据插入
插入数据的代码如下:
运行效果如图2.2.1所示,运行成功。
图2.2.1
修改“Student”的映射文件,将“Class”属性修改为不允许空:
运行效果如图2.2.1所示,抛出“not-null property references a null or transient value”的异常。
图2.2.1
我修改单元测试代码:
运行效果图2.2.2所示,运行成功。
图2.2.2
我们修改保存“Student”和“Class”实例的先后顺序:
运行效果图2.2.3所示,同样抛出“not-null property references a null or transient value”的异常。
图2.2.3
我们修改一下“Class”的映射文件:
然后修改单元测试的代码:
运行效果如图2.2.4所示,运行成功,并且没有生成“update”语句,只生成“insert into”语句。
图2.2.4
这样,保存“一”的这端(“Class”实例),就能够将“一”得那端(“Student”)连带保存。其中映射文件中的“inverse”的属性是反转的意思,就是将操作交给双向关联关系中的另一端。
我们细观察到生成的SQL语句,只生成了“insert into”语句,这样,执行效率就变的高了。
2.3 双向关联映射的数据查询
编写单元测试的代码:
运行效果如图2.3.1所示,比较以往使用SQL语句编程的代码后,发现调用“一对多”关联映射的集合变得如此方便。
单向关联映射
双向关联映射
一、单向关联映射
1.1 单向关联映射的描述
让我们回顾一下之前讲的《多对一关联映射》,如图1.1.1所示,其实“一对多”关联映射就是“多对一”关联映射相反的映射。
图1.1.1
至于“一对多”单向关联映射的代码如下:
public class Student { public virtual int? ID { get; set; } public virtual string Name { get; set; } } public class Class { public virtual int? ID { get; set; } public virtual string Name { get; set; } public virtual IList<Student> Students { get; set; } }
映射文件如下:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Student" table="T_Student" lazy="true" > <id name="ID" type="int" column="StudentID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> </class> </hibernate-mapping> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Class" table="T_Class" lazy="true" > <id name="ID" type="int" column="ClassID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <bag name="Students"> <key column="ClassID"/> <one-to-many class="Student"/> </bag> </class> </hibernate-mapping>
我们看到“Class”类中,有名为“Students” 的属性,其类型是IList<Student>。在映射文件中,我们使用<bag>和<one-to-many>标签来描述“一对多”关联映射。
1.2 单向关联映射的数据插入
单元测试类的代码如下:
[TestFixture] public class OneToManyTest { private ISessionFactory sessionFactory; public OneToManyTest() { log4net.Config.XmlConfigurator.Configure(); } [SetUp] public void Init() { var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml"); sessionFactory = cfg.BuildSessionFactory(); } [Test] public void SaveTest() { using (ISession session = this.sessionFactory.OpenSession()) { var liu = new Student { Name = "刘冬" }; var zhang = new Student { Name = "张三" }; var cls = new Class { Name = "1班" }; cls.Students = new List<Student> { liu, zhang }; ITransaction tran = session.BeginTransaction(); try { session.Save(liu); session.Save(zhang); session.Save(cls); tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw ex; } } } }
我们配置log4net输出SQL语句:
<?xml version="1.0"?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <!--log4net配置--> <log4net debug="true"> <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender"> <layout type="log4net.Layout.PatternLayout"> <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" /> </layout> </appender> <root> <level value="ALL" /> <appender-ref ref="RollingLogFileAppender" /> </root> </log4net> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>
运行效果如图1.2.1所示,先成“insert into”语句,然后生成“update”语句修改外键。
图1.2.1
从图1.2.1中,我们能够观察到,如果“一对多”的外键不允许空,就有可能插入不成功。
我们将映射文件稍作修改:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Class" table="T_Class" lazy="true" > <id name="ID" type="int" column="ClassID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <!--设置为不可空--> <bag name="Students"> <key column="ClassID" not-null="true"/> <one-to-many class="Student"/> </bag> </class> </hibernate-mapping>
运行效果如图1.2.2所示,抛出“ClassID”不允许插入NULL的异常。
图1.2.2
二、双向关联映射
2.1 双向关联映射的描述
我们修改一下代码,来实现“一对多”双向关联映射:
public class Student { public virtual int? ID { get; set; } public virtual string Name { get; set; } public virtual Class Class { get; set; } }
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Student" table="T_Student" lazy="true" > <id name="ID" type="int" column="StudentID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <many-to-one name="Class" column="ClassID" /> </class> </hibernate-mapping>
“一对多”双向关联映射指的是:在“一”的这端(“Class”类这端)包含“多”的属性(“Students”);在“多”的这端包含“一”的属性(“Class”属性)。这样两个类构成的循环引用就是双向关联映射。
2.2 双向关联映射的数据插入
插入数据的代码如下:
[Test] public void SaveTest() { using (ISession session = this.sessionFactory.OpenSession()) { var liu = new Student { Name = "刘冬" }; var zhang = new Student { Name = "张三" }; var cls = new Class { Name = "1班" }; cls.Students = new List<Student> { liu, zhang }; ITransaction tran = session.BeginTransaction(); try { session.Save(cls); session.Save(liu); session.Save(zhang); tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw ex; } } }
运行效果如图2.2.1所示,运行成功。
图2.2.1
修改“Student”的映射文件,将“Class”属性修改为不允许空:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Student" table="T_Student" lazy="true" > <id name="ID" type="int" column="StudentID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <!--不允许空--> <many-to-one name="Class" column="ClassID" not-null="true"/> </class> </hibernate-mapping>
运行效果如图2.2.1所示,抛出“not-null property references a null or transient value”的异常。
图2.2.1
我修改单元测试代码:
[Test] public void SaveTest() { using (ISession session = this.sessionFactory.OpenSession()) { var liu = new Student { Name = "刘冬" }; var zhang = new Student { Name = "张三" }; var cls = new Class { Name = "1班" }; cls.Students = new List<Student> { liu, zhang }; liu.Class = cls; zhang.Class = cls; ITransaction tran = session.BeginTransaction(); try { //先保存班级 session.Save(cls); //后保存学生 session.Save(liu); session.Save(zhang); tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw ex; } } }
运行效果图2.2.2所示,运行成功。
图2.2.2
我们修改保存“Student”和“Class”实例的先后顺序:
[Test] public void SaveTest() { using (ISession session = this.sessionFactory.OpenSession()) { var liu = new Student { Name = "刘冬" }; var zhang = new Student { Name = "张三" }; var cls = new Class { Name = "1班" }; cls.Students = new List<Student> { liu, zhang }; liu.Class = cls; zhang.Class = cls; ITransaction tran = session.BeginTransaction(); try { //先保存学生 session.Save(liu); session.Save(zhang); //后保存班级 session.Save(cls); tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw ex; } } }
运行效果图2.2.3所示,同样抛出“not-null property references a null or transient value”的异常。
图2.2.3
我们修改一下“Class”的映射文件:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain"> <class name="Class" table="T_Class" lazy="true" > <id name="ID" type="int" column="ClassID"> <generator class="native"/> </id> <property name="Name" type="string"> <column name="Name" length="50"/> </property> <!--设置为不可空--> <bag name="Students" inverse="true" cascade="all"> <key column="ClassID" not-null="true"/> <one-to-many class="Student"/> </bag> </class> </hibernate-mapping>
然后修改单元测试的代码:
[Test] public void SaveTest() { using (ISession session = this.sessionFactory.OpenSession()) { var liu = new Student { Name = "刘冬" }; var zhang = new Student { Name = "张三" }; var cls = new Class { Name = "1班" }; cls.Students = new List<Student> { liu, zhang }; liu.Class = cls; zhang.Class = cls; ITransaction tran = session.BeginTransaction(); try { //只保存班级 session.Save(cls); tran.Commit(); } catch(Exception ex) { tran.Rollback(); throw ex; } } }
运行效果如图2.2.4所示,运行成功,并且没有生成“update”语句,只生成“insert into”语句。
图2.2.4
这样,保存“一”的这端(“Class”实例),就能够将“一”得那端(“Student”)连带保存。其中映射文件中的“inverse”的属性是反转的意思,就是将操作交给双向关联关系中的另一端。
我们细观察到生成的SQL语句,只生成了“insert into”语句,这样,执行效率就变的高了。
2.3 双向关联映射的数据查询
编写单元测试的代码:
[Test] public void SelectTest() { using (ISession session = this.sessionFactory.OpenSession()) { var cls = session.Get<Class>(1); foreach (var item in cls.Students) { Console.WriteLine("学生名为:{0}", item.Name); Console.WriteLine("班级名为:{0}", item.Class.Name); } } }
运行效果如图2.3.1所示,比较以往使用SQL语句编程的代码后,发现调用“一对多”关联映射的集合变得如此方便。
相关文章推荐
- hdu 1595 find the longest of the shortest (dijkstra + spfa)
- javaWeb中文乱码 解决方法
- IOS开发中图片资源使用png还是jpg格式
- C++读取windows系统性能技术器(PDH)
- 如何获取当前执行Assembly的位置
- 通过camera控制闪光灯
- jquery 设置select 默认值
- C语言之内存四区1
- laravel yii 区别 symfony route 参数
- 15个实用的Linux find命令示例
- java求树根
- AngularJS中页面间传值
- 2015个人项目(修改除法要求)
- Java HashMap、LinkedHashMap
- Java中一个对象的实例化过程
- 15_09_15 ( NullPointerException ) 空指针异常
- nginx配置详细说明
- fileoper.py
- Python Paramiko模块安装和使用
- 地图的相关使用(定位,地理编码,导航)