1.映射多对多双向关联关系
* 多对多关联 把多对多映射拆成两个一对多映射
* 多对多的实体关系模型也是很常见的,比如学生和课程的关系。一个学生可以选修多门课程,一个课程可以被多名学生选修。在关系型数据库中对于多对多关联关系的处理一般采用中间表的形式,将多对多的关系转化成两个一对多的关系。
* 双向 n-n 关联需要两端都使用集合属性
双向n-n关联必须使用中间表
集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类
在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同
<!--table为中间表-->
<set name="courses" table="student_course" inverse="true">
<!--coursees这个集合中放置的是课程对象
通过中间表的学生id,查询该学生要学习的课程
select cid from student_course where sid=1
-->
<key column="sid"/>
<!-- class表示coursees集合存放的是什么类型的对象
column="cid":通过课程id获取课程对象
select id,name from course where id=cid;
-->
<many-to-many class="cn.itcast.many2many.Course" column="cid"/>
</set>
* set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性
* 其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名
* 因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a
* 对于双向 n-n 关联, 须把其中一端的 inverse 设置为 true, 否则可能会造成主键冲突
* class表示coursees集合存放的是什么类型的对象 private Set<Course> courses=new HashSet(0); hibernate凭这个不知道coruses集合放的是什么类型
1.1解除1号学生和1号课程的关联关系
@Test
public void removeMany2Many(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Student s1=(Student)session.get(Student.class, 1);
Course c1=(Course)session.get(Course.class, 1);
s1.getCourses().remove(c1);
c1.getStudentes().remove(s1);
tx.commit();
session.close();
}
* <set name="courses" table="student_course" inverse="true"> 没有inverse这个属性 两条delete语句 有这个属性 s1.getCourses().remove(c1); 写了跟没写是一样的
* inverse="true"主控方为对端
1.2 改变1号学生和2号课程的关联关系,改为1号学生和1号课程
@Test
public void changeMany2Many(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Student s1=(Student)session.get(Student.class, 1);
Course c1=(Course)session.get(Course.class, 1);
Course c2=(Course)session.get(Course.class, 2);
s1.getCourses().remove(c2);
c2.getStudentes().remove(s1);
s1.getCourses().add(c1);
c1.getStudentes().add(s1);
tx.commit();
session.close();
}
* 不是一条update语句,而是一条insert语句,在一条delete语句
* <set name="courses" table="student_course" inverse="true"> 没有inverse这个属性 抛异常,没有主控方,中间表联合主键,两条inset语句,主键冲突 有这个属性s1.getCourses().remove(c2); s1.getCourses().add(c1);写了跟没写是一样的
* 对于双向 n-n 关联, 须把其中一端的 inverse 设置为 true, 否则可能会造成主键冲突
1.3 删除2号学生
@Test
public void removeStudent(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Student s2=(Student)session.get(Student.class, 2);
session.delete(s2);
tx.commit();
session.close();
}
* 删除2号学生,因为2号学生在中间表有外键关联,所以会抛出异常
1.4 删除1号课程
@Test
public void removeCourse(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Course c1=(Course)session.get(Course.class, 1);
session.delete(c1);
tx.commit();
session.close();
}
* 删除1号课程.这里能删除,因为课程是主控方法
* 能删除1号课程
* 能删除1号课程对应的中间表信息,但不能是删除学生表的信息
1.5 删除1号课程的同时,要把1号和2号学生删掉?
@Test
public void removeCourseCase(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Course c1=(Course)session.get(Course.class, 1);
session.delete(c1);
tx.commit();
session.close();
}
* 课程表
* 学生表
* 中间表
* 在课程端 还要加上级联才能删除 <set name="studentes" table="student_course" cascade="delete">
* 如果 1 2 号学生还关联其他课程则不能删除 中间表外键约束
2.映射一对一外键双向关联
* 一对一关联指两个表之间的记录是一一对应的关系。分为两种:外键关联和主键关联
* 比如一家公司(Company)和它所在的地址(Address)。在业务逻辑中要求一家公司只有唯一的地址,一个地址也只有一家公司
Company.hbm.xml
<!-- 一对一外键关联
many-to-one:使用many-to-one
unique="true":设置companyfk表的外键唯一 addressid integer unique,
-->
<many-to-one name="address" column="addressid" unique="true"/>
Address.hbm.xml
<!-- 映射一对一
property-ref(互相引用):company属性管理的address这个属性
property-ref的值为Company这个类中的address属性(可以不加)
-->
<one-to-one name="company" property-ref="address"/>
* 对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加 many-to-one 元素。为 many-to-one元素增加 unique=“true” 属性来表示为1-1关联, 并用name属性来指定关联属性的属性名
* 另一端需要使用one-to-one元素,该元素使用 property-ref(可以不加) 属性指定使用被关联实体主键以外的字段作为关联字段
2.1测试唯一性
@Test
public void testUnique(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Company c=new Company();
c.setName("天空网");
//获取一号地址
Address address=(Address)session.get(Address.class, 1);
//建立关联
c.setAddress(address);
address.setCompany(c);
//不能保存
session.save(c);
tx.commit();
session.close();
}
* 外键唯一,重复冲突
2.2.映射一对一主键双向关联
* 一对一的另一种解决方式就是主键关联,在这种关联关系中,要求两个对象的主键必须保持一致,通过两个表的主键建立关联关系,无须外键参与
Company.hbm.xml
<!-- 配置Company和地址的一对一关联 -->
<one-to-one name="address"/>
Address.hbm.xml
<class name="cn.itcast.one2onepk.Address" table="addresspk">
<id name="id" type="integer">
<column name="id"/>
<!--
foreign:表示addresspk表的主键的生成参照另一个表(companypk的)主键
这里addresspk表的主键值来源于companypk表的主键,不是该addresspk表自己生成
-->
<generator class="foreign">
<!--
property:该Address类对应表主键的生成,参照该类中的属性company
-->
<param name="property">company</param>
</generator>
</id>
<property name="city" type="string">
<column name="city"/>
</property>
<property name="country" type="string">
<column name="country"/>
</property>
<!--one-to-one配置Address和Company的一对一关联
constrained="true":为addresspk增加外键约束
-->
<one-to-one name="company" constrained="true"/>
</class>
* 基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. <param> 子元素指定使用当前持久化类的哪个属性作为 “对方”
* 采用foreign主键生成器策略的一端增加 one-to-one 元素映射关联属性,其 one-to-one 属性还应增加 constrained=“true” 属性;另一端(company)增加one-to-one元素映射关联属性
* constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键