package com.itheima12.hibernate.onetomanysingle;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.itheima12.hibernate.domain.Classes;
import com.itheima12.hibernate.domain.Student;
import com.itheima12.hibernate.utils.HibernateUtils;
/**
* 一对多的单项
* @author zd
* 一般操作
1、保存班级
2、保存学生
3、保存班级,保存学生
级联操作
4、保存班级级联保存学生
5、保存班级级联更新学生
6、更新班级级联保存学生
7、更新班级级联更新学生
8、删除班级级联删除学生
9、在班级有级联save-update的情况下,从关联得到学生,并且删除学生?
关系操作
8、已经存在一个班级,新建一个学生,把该学生加入到该班级(建立关系操作)
9、已经存在一个学生,新建一个班级,把该学生加入到该班级(建立关系操作)
10、已经存在一个学生,已经存在一个班级,把该学生加入到该班级
11、已经存在一个学生,把一个学生从一个班级转移到另外一个班级
在一的一方维护关系的时候,总会发出维护关系的update语句,该update语句就是更新外键
级联和关系的混合:
12、在删除班级的时候,解除班级和学生之间的关系
*/
public class OneToManySingleTest extends HibernateUtils{
@Test
//保存一个班级对象
public void testSaveClasses(){
//我继承自HibernateUtils 我就能直接调用getCurrentSession
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setName("黑马JavaEE+hadoop的12期");
classes.setDescription("牛");
session.save(classes);
transaction.commit();
}
@Test
//保存一个学生对象
public void testSaveStudent(){
//我继承自HibernateUtils 我就能直接调用getCurrentSession
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("班长");
student.setDescription("带头大哥");
session.save(student);
transaction.commit();
}
@Test
//想保存一个班级和学生
public void testSaveClassesAndSaveStudent(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setName("黑马视频班");
classes.setDescription("野牛");
Student student = new Student();
student.setName("班秘");
student.setDescription("凤姐");
//保存两次
session.save(classes);
session.save(student);
transaction.commit();
}
这个运行起来 还是没有关联起来,而且一般不这么做,因为假设有100个学生对象的话,save100次。。。。
如果想要是进行级联操作的话,需要配置classess的配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.itheima12.hibernate.domain.Classes">
<id name="cid" length="5">
<generator class="increment"></generator>
</id>
<property name="description" length="50"></property>
<property name="name" length="20"></property>
<!--
set元素针对的就是Classes类中的Set属性
cascade 级联操作
null 默认值
save-update
在保存classes对象的时候,针对student进行保存或者更新的操作
在更新classes对象的时候,针对student进行保存或者更新的操作
all
delete
inverse 关系操作
default:classes维护classes与student之间的关系
true: classes不维护classes与student之间的关系
false: classes维护classes与student之间的关系
-->
<set name="students" cascade="save-update" inverse="true">
<!--
外键
告诉hibernate,通过cid就可以建立classes与student之间的关联
-->
<key>
<column name="cid"></column>
</key>
<!--
告诉hibernate,Classes类中的set集合中存放的是哪个元素
-->
<one-to-many class="com.itheima12.hibernate.domain.Student"/>
</set>
</class>
</hibernate-mapping>
配置好xml文件以后,继续编写测试类
/**
* 在保存班级的时候,级联保存学生
*/
@Test
public void testSaveClasses_Cascade_Save_Student(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = new Classes();
classes.setName("黑马视频班");
classes.setDescription("野牛");
Student student = new Student();
student.setName("班秘");
student.setDescription("凤姐");
//建立classes与student之间的关联 但是如果在xml文件中没有配置 级联的话就会出现错误
//我先准备一个set集合
Set<Student> students = new HashSet<Student>();
students.add(student);
classes.setStudents(students);
session.save(classes); //显示保存的是classes,把保存student称为隐式保存
//这里有个很难排 transient 临时的
transaction.commit();
}
/**
* 在更新班级的时候,级联更新学生
* sessin.flush的时候
* 1、检查一级缓存中所有的持久化状态的对象
* 判断发出insert语句或者update语句
* 2、检查所有的持久化对象的关联对象
* 如果关联对象是由临时状态转化过来的,则对关联对象发出insert语句
* 如果关联对象是从数据库中提取出来的,则对照副本,决定是否发出update语句
*/
@Test
public void testUpdateClasses_Cascade_Update_Student(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 2L);//持久化类
Set<Student> students = classes.getStudents();//持久化类
for (Student student : students) {//把每一个student对象(关联对象)也放入到了一级缓存中
student.setDescription("bb");
}
students = classes.getStudents();//再次执行的话 发现并没有发出sql语句 说明
for (Student student : students) {//把每一个student对象(关联对象)也放入到了一级缓存中
student.setDescription("bb");
}
transaction.commit();
}
/**
* 在更新班级的时候,添加学生
*/
@Test
public void testUpdateClasses_Cascade_Save_Student(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
/*
* 给cid为2的班级添加一个学生
*/
//首先我得得到cid为2的班级
Classes classes = (Classes)session.get(Classes.class, 2L);
//接下来我得整一个学生
Student student = new Student();
student.setName("美女1");
student.setDescription("小林志玲");
/**
* 建立班级与学生的关联
* Set<Student> students = new HashSet<Student>();
* students.add(student);
* 这个不行 会全覆盖 涉及到了关系操作
*/
classes.getStudents().add(student);
transaction.commit();
}
总结一下:在保存或者更新班级的时候,我操作学生
/**
* 在更新班级的时候,级联保存学生,并且维护关系
* Hibernate:
select
classes0_.cid as cid0_0_,
classes0_.description as descript2_0_0_,
classes0_.name as name0_0_
from
Classes classes0_
where
classes0_.cid=?
Hibernate:
select
students0_.cid as cid0_1_,
students0_.sid as sid1_,
students0_.sid as sid1_0_,
students0_.description as descript2_1_0_,
students0_.name as name1_0_
from
Student students0_
where
students0_.cid=?
Hibernate:
select
max(sid)
from
Student
Hibernate:
因为在Classes.hbm.xml文件中设置了级联
<set name="students" cascade="save-update">
insert
into
Student
(description, name, sid)
values
(?, ?, ?)
Hibernate:
因为在Classes.hbm.xml文件中,inverse没有写,默认classes维护classes与student之间的关系
所以发出了更新关系的update语句 怎么体现维护关系 通过更新外键关系
这个语句发出的关键是 inverse
update
Student
set
cid=?
where
sid=?
*/
@Test
public void testUpdateClasses_Cascade_Save_Student_Inverse(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
/*
* 给cid为2的班级添加一个学生
*/
Classes classes = (Classes)session.get(Classes.class, 2L);
Student student = new Student();
student.setName("美女1");
student.setDescription("小林志玲");
/**
* 建立班级与学生的关联
*/
classes.getStudents().add(student);
transaction.commit();
}
/**
* 已经存在一个班级cid为1,已经存在一个学生,已经存在另外一个班级cid为2,该学生从cid为1的班级转到cid为2的班级
*/
@Test
public void testTransform(){
/**
* 1、把cid为1,2和sid为1的对象提取出来
*/
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//Classes classes1 = (Classes)session.get(Classes.class, 1L);
Classes classes2 = (Classes)session.get(Classes.class, 2L);//发出查询的sql语句
Student student = (Student)session.get(Student.class, 1L);//发出查询的sql语句
//解除 classes1与student之间的关系
//classes1.getStudents().remove(student);
//建立classes2与student之间的关系
classes2.getStudents().add(student);
transaction.commit();
}
/**
* 解除该班级和该班级中的所有的学生之间的关系
*/
@Test
public void testRealseR(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
/**
* 解除cid为2的班级和所有的学生之间的关系
*/
Classes classes = (Classes)session.get(Classes.class,2L);
classes.setStudents(null);
transaction.commit();
}
/**
* 解除该班级和所有的学生之间的关系,再建立该班级和一些学生之间的关系
*/
@Test
public void testRealseAllR_BuildSomeR(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
//解除cid为2的班级和所有的学生之间的关系
Classes classes = (Classes)session.get(Classes.class, 2L);
classes.setStudents(null);
Student student1 = new Student();
student1.setName("王二麻子");
Student student2 = new Student();
student2.setName("隔壁老李");
Set<Student> students = new HashSet<Student>();
students.add(student2);
students.add(student1);
classes.setStudents(students);
transaction.commit();
}
/**
* 删除一个班级
* 在删除班级之前,解除班级和学生之间的关系
* Hibernate:
select
classes0_.cid as cid0_0_,
classes0_.description as descript2_0_0_,
classes0_.name as name0_0_
from
Classes classes0_
where
classes0_.cid=?
Hibernate:
因为classes负责维护关系,所以该语句就是解除关系的sql语句
update
Student
set
cid=null
where
cid=?
Hibernate:
delete
from
Classes
where
cid=?
*/
@Test
public void testDeleteClasses_1(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 1L);
session.delete(classes);
transaction.commit();
}
/**
* 在 Classes.hbm.xml文件中
* <set name="students" cascade="all">
* 在删除班级的时候,级联删除学生
*/
@Test
public void testDeleteClasses_2(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 1L);
session.delete(classes);
transaction.commit();
}
/**
* 删除一个学生,但是该学生必须从班级中提取出来
* 因为在Classes.hbm.xml文件中
* <set name="students" cascade="save-update">
* classes针对student是关联的
* 而程序中的student对象是从classes中提取出来的关联对象,所以不能删除
*/
@Test
public void testError(){
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Classes classes = (Classes)session.get(Classes.class, 1L);
Set<Student> students = classes.getStudents();
//classes.setStudents(null);//解除关系
for (Student student : students) {
session.delete(student);
}
transaction.commit();
}
}
这里来解释一下:为什么每次执行都跟着一个update语句
我要建立联系班级和学生的联系,在hibernate面向对象操作,cascade是级联操作,hibernate当中有个属性叫做inverse
inverse 关系操作
- default:classes维护classes与student之间的关系
- true: classes不维护classes与student之间的关系
- false: classes维护classes与student之间的关系