一、 多对一单向关联
*****************************************************************************************
类:
public class Classes implements Serializable {
private Long cid;
private Set<Student> students;
// set/get属性
}
public class Student implements Serializable {
private Long sid;
// set/get属性
}
----------------------------------------------------------------------------------------
配置文件:
Student.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.oneToMany.domain.Student">
<id name="sid" length="3">
<generator class="increment"></generator>
</id>
</class>
</hibernate-mapping>
----------------------------------------------------------------------------------------
Classes.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.oneToMany.domain.Classes">
<id name="cid" length="5" type="java.lang.Long">
<generator class="increment"></generator>
</id>
<set name="students" cascade="all" inverse="true">
<!-- key用来描述外键 -->
<key>
<column name="cid"></column>
</key>
<one-to-many class="cn.itcast.oneToMany.domain.Student"/>
</set>
</class>
</hibernate-mapping>
注:
set元素对应类中的set集合,通过set元素使classes表和student表建立关联
属性:
cascade: 级联操作 描述对象和对象之间的关系
save-update
当保存班级的时候,对学生进行怎样的操作:
若学生对象在数据库中没有对应的值,这个时候就会执行save操作
若学生对象在数据库中有对应的值,将该持久化对象与快照进行比对,
如果一致,则什么也不做,如果不不一致,则发出update语句
delete
all
注: 把session.save/update一个对象的操作为显式操作,级联的操作为隐式操作
当隐式操作一个对象的时候,必须在配置文件中设置cascade属性
inverse: 维护关系(即外键) 描述对象与外键之间的关系
true 不维护关系
false 维护关系 默认值
default false
注: inverse在那个对象的配置文件,就是该对象维护另一个对象
inverse所在的映射文件classes对classes与student之间的关系是否进行维护
维护关系体现在两点:
1、在映射文件中inverse的值必须是false/default
2、必须在客户端代码上建立两者之间的关系,更新外键
子元素:
key是通过外键的形式使俩张表建立关联,column对应外键的字段
one-to-many是通过类的形式让俩个类建立关联
*****************************************************************************************
例子:
*****************************************************************************************
public class OneToManySingleTest {
// 1. 新建一个班级
@Test
public void testSaveClasses(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setCname("传智上海云一期SB");
classes.setDescription("很牛SB");
session.save(classes);
transaction.commit();
session.close();
}
// 2. 新建一个学生
@Test
public void testSaveStudent(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setSname("班长");
student.setDescription("技术很牛");
session.save(student);
transaction.commit();
session.close();
}
// 3.0. 方式1: 新建一个班级的同时新建一个学生
@Test
public void testSaveClass_Student(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setCname("传智上海云二期");
classes.setDescription("很牛X");
Student student = new Student();
student.setSname("班长");
student.setDescription("技术很牛X");
session.save(classes);
session.save(student); // 该方式若有n个学生,则必须保存n次
transaction.commit();
session.close();
}
// 3.1. 方式2: 新建一个班级的同时新建一个学生 在保存班级的同时,级联保存学生
@Test
public void testSaveClass_cascade_Student_save(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setCname("传智上海云三期");
classes.setDescription("很牛XX");
Student student = new Student();
student.setSname("班长");
student.setDescription("技术很牛XX");
// 建立classes与student之间的联系
Set<Student> students = new HashSet<Student>();
students.add(student);
classes.setStudents(students);
// 保存班级,同时级联保存了学生; 该方式保存学生只须保存一个集合即可
session.save(classes);
transaction.commit();
session.close();
}
// 3.2. 方式2: 新建一个班级的同时新建一个学生 在保存班级的同时,级联更新学生
@Test
public void testSaveClass_cascade_Student_update(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setCname("传智上海云四期");
classes.setDescription("很牛XXX");
Student student = (Student) session.get(Student.class, 1L);
student.setSname("小班长"); // 与快照进行比对,修改了,所以执行update语句
// 建立classes与student之间的联系
Set<Student> students = new HashSet<Student>();
students.add(student);
classes.setStudents(students);
// 保存班级,同时级联保存了学生; 该方式保存学生只须保存一个集合即可
session.save(classes);
transaction.commit();
session.close();
}
// 3.3. 已经存在一个班级,已经存在一些学生,新建一个学生并添加到该班级中
@Test
public void testUpdateClasses_Cascade_Student_Save(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setSname("班花");
student.setDescription("稀有人物");
Classes classes = (Classes) session.get(Classes.class, 5L);
classes.getStudents().add(student);
transaction.commit();
session.close();
}
// 3.4. 已经存在一个班级,已经存在一些学生,修改该班级的学生信息
@Test
public void testUpdateClasses_Cascade_Student_Update(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 5L);
Set<Student> students = classes.getStudents(); // 班号为5的班级的学生集合
for(Student student:students){
student.setDescription("亚历山大");
}
transaction.commit();
session.close();
}
/**
* 4. 已经存在一个班级,新建一个学生,建立学生与班级的关系
* 通过更新班级级联保存学生: cascade
* 建立班级和学生之间的关系: inverse
*/
@Test
public void testSaveStudent_R_1(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 1L);
Student student = new Student();
student.setSname("技术班长");
student.setDescription("大牛");
classes.getStudents().add(student);
transaction.commit();
session.close();
}
// 5. 已经存在一个学生,新建一个班级,把学生加入到该班级中
@Test
public void testSaveClassess_R(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = (Student) session.get(Student.class, 2L);
Classes classes = new Classes();
classes.setCname("老毕基础加强班");
classes.setDescription("必看,杀手锏");
Set<Student> students = new HashSet<Student>();
students.add(student);
classes.setStudents(students);
session.save(classes);
transaction.commit();
session.close();
}
/**
* 6. 把一个学生从一个班级转移到另一个班级中
* Hibernate: select classes0_.cid as cid0_0_, classes0_.cname as cname0_0_, classes0_.description as descript3_0_0_ from Classes classes0_ where classes0_.cid=?
Hibernate: select classes0_.cid as cid0_0_, classes0_.cname as cname0_0_, classes0_.description as descript3_0_0_ from Classes classes0_ where classes0_.cid=?
Hibernate: select student0_.sid as sid1_0_, student0_.sname as sname1_0_, student0_.description as descript3_1_0_ from Student student0_ where student0_.sid=?
Hibernate: select students0_.cid as cid0_1_, students0_.sid as sid1_, students0_.sid as sid1_0_, students0_.sname as sname1_0_, students0_.description as descript3_1_0_ from Student students0_ where students0_.cid=?
Hibernate: select students0_.cid as cid0_1_, students0_.sid as sid1_, students0_.sid as sid1_0_, students0_.sname as sname1_0_, students0_.description as descript3_1_0_ from Student students0_ where students0_.cid=?
Hibernate: update Student set cid=null where cid=? and sid=?
Hibernate: update Student set cid=? where sid=?
*/
@Test
public void testTransformClass(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// 根据数据库语句,不需要解除关系,直接该外键就可以 所以可以省略这俩步
// Classes classes5 = (Classes) session.get(Classes.class, 5L);
Classes classes6 = (Classes) session.get(Classes.class, 6L);
Student student = (Student) session.get(Student.class, 1L);
// classes5.getStudents().remove(student);
classes6.getStudents().add(student);
transaction.commit();
session.close();
}
// 8. 解除一个班级和一些学生的关系
@Test
public void testR_Some(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 1L);
Set<Student> students = classes.getStudents();
// 因为要用到角标获取元素,而set无法获取到角标,所以须将set集合转换成list集合
List<Student> sList = new ArrayList<Student>(students);
// 将id为1号的班级中id为2,3号的学生解除关系
for(int i=0; i<sList.size(); i++){
if(sList.get(i).getSid().longValue()==2||sList.get(i).getSid().longValue()==3){
sList.remove(sList.get(i));
i--; // 注: remove取出元素,需要--
}
}
// 再将list集合还原成set集合,并建立与班级的关系
students = new HashSet<Student>(sList);
classes.setStudents(students);
transaction.commit();
session.close();
}
// 9. 解除该班级和所有学生的关系
@Test
public void testRealseAll(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 1L);
// 方式1 先查询,再清空
// Set<Student> students = classes.getStudents();
// students.clear();
// 方式2 直接将集合置为null,效率更高
classes.setStudents(null);
transaction.commit();
session.close();
}
// 12.删除学生
@Test
public void testDeleteStudent(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = (Student) session.get(Student.class, 3L);
session.delete(student);
transaction.commit();
session.close();
}
// 13.1.删除班级 在删除班级之前,解除班级与学生的关系
// 注: 此时Classes.hbm.xml配置文件中名称为students的set元素的inverse属性为默认值false 维护关系
// 所以在删除classes的时候hibernate内部会自动解除班级与学生的关系,然后再删除classes
// 当Classes.hbm.xml配置文件中名称为students的set元素的inverse属性为true 不维护关系时,单项关联无法解决
@Test
public void testDeleteClasses_1(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 2L);
session.delete(classes);
transaction.commit();
session.close();
}
// 13.2. 删除班级 删除班级的同时删除学生
// 注: 此时cascade为all inverse为true
@Test
public void testDeleteClasses_cascade(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 3L);
session.delete(classes);
transaction.commit();
session.close();
}
}
*****************************************************************************************
二 一对多的双向关联
*****************************************************************************************
类:
public class Classes implements Serializable {
private Long cid;
private Set<Student> students;
// set/get属性
}
public class Student implements Serializable {
private Long sid;
private Classes classes;
// set/get属性
}
----------------------------------------------------------------------------------------
配置文件:
Student.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.oneToMany.domain.Student">
<id name="sid" length="3">
<generator class="increment"></generator>
</id> 、
<!-- 多对一 column 外键 -->
<many-to-one name="classes" class="cn.itcast.oneToMany.domain.Classes" column="cid" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
注: 一对多的操作,对多的一方操作不需要维护外键(inverse为true),会自动维护;不会发出update语句来维护外键
若对一的一方操作,需要维护外键(inverse为false),要发出update维护外键的语句
故一对多对多的一方操作,自动维护外键的关系,效率高;
----------------------------------------------------------------------------------------
Classes.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.oneToMany.domain.Classes">
<id name="cid" length="5" type="java.lang.Long">
<generator class="increment"></generator>
</id>
<set name="students" cascade="all" inverse="true">
<key>
<column name="cid"></column>
</key>
<one-to-many class="cn.itcast.oneToMany.domain.Student"/>
</set>
</class>
</hibernate-mapping>
*****************************************************************************************
例:
*****************************************************************************************
public class OneToManyDoubleTest {
// 3. 新建一个班级的同时新建一个学生
@Test
public void testSaveStudent_Cascade_Classes_Save(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setSname("班秘");
student.setDescription("班长的秘书");
Classes classes = new Classes();
classes.setCname("老毕的亲子班");
classes.setDescription("都是老毕的亲戚");
student.setClasses(classes);
session.save(student);
transaction.commit();
session.close();
}
// 4. 已经存在一个班级,新建一个学生,建立学生与班级的关系
// 注: 一对多,从多的一端维护关系效率较高,因为不必维护外键,不需发出update语句
@Test
public void testSaveStudent_R(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setSname("朱有才");
student.setDescription("才华横溢");
Classes classes = (Classes) session.get(Classes.class, 6L);
student.setClasses(classes);
session.save(student);
transaction.commit();
session.close();
}
// 8. 解除一个班级和一些学生的关系
@Test
public void testRelease_R(){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes) session.get(Classes.class, 6L);
Student student1 = (Student) session.get(Student.class, 1L);
Student student6 = (Student) session.get(Student.class, 6L);
student1.setClasses(null);
student6.setClasses(null);
transaction.commit();
session.close();
}
}
*****************************************************************************************
三、总结
1、 如果让一的一方维护关系,取决于的因素有:
1.在一的一方的映射文件中,set的inverse属性为default/false
2.在客户端的代码中,通过一的一方建立关系
3.session.save/update方法是用来操作表的,和操作关系无关
2、 采用级联的方式通过保存一个对象从而保存关联对象
1.如果session.save操作的对象是A,这个时候应该看A.hnm.xml文件,看set元素中的cascade属性是否设置有级联保存
2.在客户端通过A建立关联
3.在客户端执行session.save(A)
3、 一对多的情况,多的一方维护关系效率比较高
1.在多的一方many-to-one中没有inverse属性
2.在客户端通过多的一方建立关联