案例:学生 vs 老师
声明:多对多的关系的实现必须要中间表来完成,这里我们建立一个middles表作为中间表;主控方我们选择student作为主控方。
关键映射配置:
Student.hbm.xml文件:
<set name="teacherSet" table="MIDDLES">
<span style="white-space:pre"> </span><key column="student_id"/>
<span style="white-space:pre"> </span><many-to-many class="Teacher" column="teacher_id"/>
</set>
Teacher.hbm.xml文件:
//表示学生是主控方
<set name="studentSet" table="MIDDLES" cascade="all" iverse="true">
<span style="white-space:pre"> </span><key column="teacher_id"/>
<span style="white-space:pre"> </span><many-to-many class="Student" column="student_id"/>
</set>
工程项目结构如下:
所涉及的jar包如下:
多对多实现开发步骤:
1,导入hibernate框架相关jar包.
2,编写sql语句,创建表.
3,涉及到的相关包.
1)cn.lsh.web.hibernate.db
2)cn.lsh.web.hibernate.utils
HibernateUtils类
3)cn.lsh.web.hibernate.domain
student/teacher/实体对应的映射文件(Student.hbm.xml/Teacher.hbm.xml)
4)cn.lsh.web.hibernate.dao
4,src目录下创建hibernate.cfg.xml映射文件 .
第一步:建表(多对多的关系的实现必须要中间表来完成,这里我们建立一个middles表作为中间表;主控方我们选择student作为主控方。)
共涉及到三个表:
1.student表
2.teacher表
3.middles表
注意:先建teacher和student表,再创建middles表
drop database if exists db3;
create database if not exists db3;
use db3;
drop table if exists student;
create table if not exists student(
id int primary key auto_increment,
name varchar(20));
drop table if exists teacher;
create table if not exists teacher(
id int primary key auto_increment,
name varchar(20));
drop table if exists middles;
create table if not exists middles(
sid int,
tid int,
constraint sid_FK foreign key(sid) references student(id),
constraint tid_FK foreign key(tid) references teacher(id),
primary key(sid,tid));
第二步:建立实体类
Teacher.java
//多方
public class Teacher {
private Integer id;
private String name;
private Set<Student> studentSet = new HashSet<Student>();//学生集合
public Teacher(){}
public Teacher(String name) {
super();
this.name = name;
}
//省略set/get方法
}
Student.java
//实体
public class Student {
private Integer id;
private String name;
//老师集合
private Set<Teacher> teacherSet = new HashSet<Teacher>();
public Student() {
}
public Student(String name) {
super();
this.name = name;
}<pre name="code" class="java"><span style="white-space:pre"> </span>//省略set/get方法<span style="white-space:pre"> </span>
第三步:在src目录下创建hibernate.cfg.xml映射文件,配置如下
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置连接信息 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/db3</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<!-- 加载实体对应的映射文件 -->
<mapping resource="cn/lsh/web/hibernate/domain/Student.hbm.xml"/>
<mapping resource="cn/lsh/web/hibernate/domain/Teacher.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第四步创建HibernateUtils工具类
package cn.lsh.web.hibernate.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
private static SessionFactory sessionFactory;
static{
//加载hibernate.cfg.xml映射文件,同时加载实体映射文件
Configuration config = new Configuration().configure();
sessionFactory = config.buildSessionFactory();
}
public static Session getSession(){
return sessionFactory.openSession();
}
}
第五步:映射关系配置
要求1:保存学生,级联保存老师,学生是主控方
实体映射文件(Student.hbm.xml):
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.lsh.web.hibernate.domain">
<class name="Student" table="STUDENT">
<id name="id" column="ID" type="integer">
<generator class="native"/>
</id>
<property name="name" column="NAME" type="string"/>
<!-- 保存学生级联保存老师,学生是主控方 -->
<set name="teacherSet" table="MIDDLES" cascade="all">
<key column="SID"/>
<many-to-many class="Teacher" column="TID"/>
</set>
</class>
</hibernate-mapping>
实体映射文件(Teacher.hbm.xml):
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.lsh.web.hibernate.domain">
<class name="Teacher" table="TEACHER">
<id name="id" column="ID" type="integer">
<generator class="native"/>
</id>
<property name="name" column="NAME" type="string"/>
<set name="studentSet" table="MIDDLES" inverse="true">
<key column="TID"/>
<many-to-many class="Student" column="SID"/>
</set>
</class>
</hibernate-mapping>
创建TeacherAndStudentDao.java类
测试:
@Test
public void test1(){//保存学生,级联保存老师,学生是主控方,SQL维护者
//2个学生
Student s1 = new Student("杰克");
Student s2 = new Student("马利");
//2个老师
Teacher t1 = new Teacher("赵");
Teacher t2 = new Teacher("蔡");
//设置单向关联
s1.getTeacherSet().add(t1);
s1.getTeacherSet().add(t2);
s2.getTeacherSet().add(t1);
s2.getTeacherSet().add(t2);
Session session = HibernateUtils.getSession();
Transaction t = session.getTransaction();
try {
t.begin();
session.save(s1);
session.save(s2);
t.commit();
} catch (Exception e) {
e.printStackTrace();
t.rollback();
}finally{
//将session一级缓存中的所有持久化状态对象清出一级缓存
//session关闭,session集合销毁
session.close();
//强行将将变量设置为null
s1 = null;
s2 = null;
t1 = null;
t2 = null;
}
}
查询数据库如下:
mysql> select * from teacher;
+----+------+
| id | name |
+----+------+
| 1 | 蔡 |
| 2 | 赵 |
+----+------+
2 rows in set (0.00 sec)
mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 杰克 |
| 2 | 马利 |
+----+------+
2 rows in set (0.00 sec)
要求2:删除1号老师,级联删除学生
修改:Teacher.hbm.xml
<!-- 设置老师为主控方,删除老师级联删除学生 -->
<set name="studentSet" table="MIDDLES" inverse="false" cascade="delete">
<key column="TID"/>
<many-to-many class="Student" column="SID"/>
</set>
TeacherAndStudentDao.java类
<span style="white-space:pre"> </span>@Test
public void test2(){//删除1号老师,级联删除学生
Session session = HibernateUtils.getSession();
Transaction t = session.getTransaction();
try{
t.begin();
Teacher t1 = (Teacher) session.get(Teacher.class,1);
session.delete(t1);
t.commit();
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
session.close();
}
}
要求3:删除1号老师,不级联删除学生【重中之重】
修改:Teacher.hbm.xml
<set name="studentSet" table="MIDDLES" inverse="true" cascade="delete">
<key column="TID"/>
<many-to-many class="Student" column="SID"/>
</set>
TeacherAndStudentDao.java类
<span style="white-space:pre"> </span>//删除1号老师,【不级联】删除学生
<span style="white-space:pre"> </span>@Test
public void test3(){
Session session = HibernateUtils.getSesison();
Transaction t = session.getTransaction();
try{
t.begin();
Teacher t1 = (Teacher) session.get(Teacher.class,1);
//分离学生和老师的双向关联
for(Student s : t1.getStudentSet()){
s.getTeacherSet().remove(t1);
}
t1.setStudentSet(null);
session.delete(t1);
t.commit();
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
session.close();
}
}
要求4:删除1号学生,不级联删除老师【重中之重】
修改Teacher.hbm.xml
<set name="studentSet" table="MIDDLES" inverse="true">
<key column="TID"/>
<many-to-many class="Student" column="SID"/>
</set>
TeacherAndStudentDao.java类:
<span style="white-space:pre"> </span>@Test
public void test4(){//删除1号学生,【不级联】删除老师
Session session = sessionFactory.openSession();
Transaction t = session.getTransaction();
try{
t.begin();
Student s1 = (Student) session.get(Student.class, 1);
//想办法,分离学生和老师的双向关联
for(Teacher t1:s1.getTeacherSet()) {
t1.getStudentSet().remove(t1);
}
s1.setTeacherSet(null);
session.delete(s1);
t.commit();
}catch(Exception e){
e.printStackTrace();
t.rollback();
}finally{
session.close();
}
}
总结:
1. 在数据库中,只有单向关系;在java中,既有单向,又有双向
2. 在多对多双向情况下,一定要拆分成二个一对多情况,此时【必须】使用inverse=true属性,如果不使用的话,会出现主键冲突的情况。
3. 多对多,在dao中,不用设置双向关联,只要主控方关联被控方就行了。
4. 上例中,相对于middles,students和teachers都是单方。
5. 不级联删除的关键代码:
//想办法,分离学生和老师的双向关联
Teacher t1 = (Teacher) session.get(Teacher.class,1);
for(Student s : t1.getStudentSet()){
s.getTeacherSet().remove(t1);
}
t1.setStudentSet(null);
session.delete(t1);