关联关系映射速览
电脑本地链接
一对多的双向关联
一般企业中很少使用删除,只是状态的变化。
inverse针对的外键
首先呢 classes这个类不用更改
一个学生对于一个班级,所以在Student中添加关联对象,实现set和get方法
在学生的映射文件中,有个其属性中,有column代表外键 class写上对应的类,这个外键指的是我通过classes 联系的student,说白了,给我一个班级,我可以得到学生。
而另一个外键指的是给我学生的姓名,我可以找班级,所以说在两个表的映射文件中,都有设置外键。
表结构不变,这样的话我就可以在hibernate客户端通过学生来操作班级了。
我们来看一下哪种效率比较高
这通过学生来建立的关联,我去找studengt的映射文件,设置cascade ,这样的话
public class OneToManyTest extends HibernateUtils{
/**
* Hibernate:
//保存一个学生 因为学生主键的生成需要查最大主键
select max(sid) from Student
Hibernate:
//保存班级信息 查询主键的最大值
select max(cid) from Classes
Hibernate:
insert into Classes (description, name, cid) values (?, ?, ?)
Hibernate:
//cid 是外键 直接插入进去了,如果是 classes级联的时候 先保存学生的一般属性 再发出条update语句更新主键值
外键直接插入进去了,无需发出update语句
insert into Student (description, name, cid, sid) values (?, ?, ?, ?)
*/
@Test
public void testSaveStudent_Cascade_Save_Classes(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("asfd");
Classes classes = new Classes();
classes.setName("12期");
//创建班级与学生之间的关联
student.setClasses(classes); //通过学生建立的关联
session.save(student);
transaction.commit();
session.close();
}
/**
* update
Student
set
description=?,
name=?,
cid=?
where
sid=?
*/
@Test
public void testTransform(){
/**
* 把sid为11的学生从cid为2的班级转移到cid为1的班级
* cid为2的班级需要查出来吗?我最终要通过student来操作,我可以setclasess为最新班级
* 我只需要得到学生
*
*/
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = (Student)session.get(Student.class, 11L);
Classes classes = (Classes)session.get(Classes.class, 1L);
//建立关系
student.setClasses(classes);
transaction.commit();
session.close();
}
/**
* 把一个学生从一个班级中移除
* 这个update语句很关键 我只是想更新外键就可以 但是结果更改了除主键以外的所有属性
* 没有inverses
* 我通过student 来维护关系或者说来做级联 他操作的就是学生信息本省 不存在维护的关系 因为值一直都有
* 所有不单独发出更新的sql语句
* update
Student
set
description=?,
name=?,
cid=?
where
sid=?
*/
@Test
/*
*我实际上删除一个学生 不需要知道学生在哪个班级 我直接设置班级为null发现一个事情 整个学生信息都变成空了
*/
public void testRemoveFromClasses(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = (Student)session.get(Student.class, 11L);
student.setClasses(null);
transaction.commit();
session.close();
}
}
来总结一下 :
一对多的双向
- 当多的一方维护关系时,不会发出更新关系的update语句,
- 而一的一方维护关系时,需要维护关系的updates,
- 一般清况下,多的一方维护关系的效率高。
也有特殊情况,实际操作的过程看需求。
多对多的关系
最简单的多对多的关系的例子很多,比如学生和老师,学生和课程。
一个学生选修多个课程 所以需要一个set集合
在course 中,有多个学生选修一个课程,
一对多:对象和集合之间的关系,或者对象和对象之间的关系。
多对一:对象与对象之间的关系
多对多“对象和集合之间的关系”
多对对 数据库中至少3张表 第三章表有两个外键
<?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.Student">
<id name="sid" length="5">
<generator class="increment"></generator>
</id>
<property name="description" length="50"></property>
<property name="name" length="20"></property>
<!--
table
第三张表
-->
<set name="courses" table="student_course" cascade="save-update">
<key>
<!--
外键 对应的就是当前类的配置文件的的主键
-->
<column name="sid"></column>
</key>
<!--
column外键
-->
<many-to-many class="com.itheima12.hibernate.domain.Course" column="cid"></many-to-many>
</set>
</class>
</hibernate-mapping>
你得告诉第三张表 拼sql语句,这两个id就是建立关联的,
同样的course也是一样的,配置配置文件。
你只要想它怎么生成sql语句 就很好理解了
内连接的多对多的查询
多对多要分析几个问题:
谁维护关系的效率最高呢? 答案是没有区别
比如我现在有个学生要选修一门课程 那么在数据库中插入到第三张表中一条数据就好了
多对多建立关系相当于在第三张表中插入一条数据,多对多解除关系相当于在第三章表中删除的话
多对多改变一条关系,相当于先删除再增加
看需求来选择谁来维护表。
用户的岗位也是多对多 一个用户有很多个岗位。
package com.itheima12.hibernate.manytomany;
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.Course;
import com.itheima12.hibernate.domain.Student;
import com.itheima12.hibernate.utils.HibernateUtils;
public class ManyToManyTest extends HibernateUtils{
/**
* 保存学生的时候,同时保存课程
* Hibernate:
select
max(sid)
from
Student
Hibernate:
select
max(cid)
from
Course
Hibernate:
insert into Student (description, name, sid) values (?, ?, ?)
Hibernate:
在Student.hbm.xml文件中
<set cascade="save-update">
insert into Course(description, name, cid) values (?, ?, ?)
Hibernate:
建立关系的sql语句
insert into student_course (sid, cid) values (?, ?)
*/
@Test
public void testSaveStudent_Cascade_Save_Course(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Student student = new Student();
student.setName("aa");
Course course = new Course();
course.setName("java基础课程");
//建立学生与课程之间的关系
Set<Course> courses = new HashSet<Course>();
courses.add(course);
student.setCourses(courses);
session.save(student);
transaction.commit();
session.close();
}
/**
* 已经存在一个课程,已经存在一个学生,建立该课程和该学生之间的关系
*/
@Test
public void testBuildR(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//把cid为2的课程和sid为1的学生提取出来
Course course = (Course)session.get(Course.class, 2L);
Student student = (Student)session.get(Student.class, 1L);
//接下来建立关系 即便学生选修的课程为空的话 也能获取的到 hibernate在内部可以创建空集合
student.getCourses().add(course);
//course.getStudent().add(class);从课程出发和冲学生出发的意义是一样的
//
transaction.commit();
session.close();
}
/**
* 已经存在一个课程,新建一个学生,建立该课程和该学生之间的关系
* 首先 我要创建一个学生 新的学生没有课程
*/
@Test
public void testSaveStudent_BuildR(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//创建新学生
Student student = new Student();
student.setName("student1");
//提取出课程信息
Course course = (Course)session.get(Course.class, 1L);
/**
* 建立了新的学生与该课程之间的关系 双向维护的时候
*/
Set<Course> courses = new HashSet<Course>();
//首先
courses.add(course);
student.setCourses(courses);
//因为学生是临时/游离状态 所以需要save一下
session.save(student);
transaction.commit();
session.close();
}
/**
* 一个学生从一门课程转到另外一门课程
*/
@Test
public void testTranform(){
/**
* sid为1的学生从课程2转到课程1
*
*/
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//首先 提取sid为1的学生
Student student = (Student)session.get(Student.class, 1L);
//能不能 Student内存在的是一个course1的集合,那么课程2中依然存在这个学生
//把课程1和课程2提取出来
Course course1 = (Course)session.get(Course.class, 1L);
Course course2 = (Course)session.get(Course.class, 2L);
//你得先干掉 不然的话都设置为null了
student.getCourses().remove(course2);//解除sid为1的学生和cid为2的课程
student.getCourses().add(course1);//建立sid为1的学生和cid为1的课程
transaction.commit();
session.close();
}
这就上面的粒子发出的sql语句,那么问题来了,这么麻烦 ,有没有优化的方法?
答案:没有!
你得用面向对象的方法来整hibernate,还好有一级缓存。
注意:如果有一个表中有千万级别的数据,那么就不能使用这个hibernate因为废了
/**
* 解除一个学生和该学生所学的所有的课程之间的关系
*/
@Test
public void testRealseAllR(){
/**
* 解除sid为1的学生和所有的课程之间的关系
*/
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//先得到学生信息
Student student = (Student)session.get(Student.class, 1L);
//设置为空就行了
student.setCourses(null);
transaction.commit();
session.close();
}
/**
* 解除一个学生和所有的课程之间的关系,再建立一些课程之间的关系
* 例如:
* 如果:我先解除这个学生和课程的所有信息,然后再 add新的课程,那么效率就非常低了
*/
@Test
public void testRealseAllRAndBuildR(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//首先得到这个学生信息
Student student = (Student)session.get(Student.class, 1L);
//整个set集合
Set<Course> courses = new HashSet<Course>();
//要建立和course3的关系
Course course = (Course)session.get(Course.class, 3L);
courses.add(course);
//把原来的student中的courses给覆盖掉了
student.setCourses(courses);
transaction.commit();
session.close();
}
}
一对一:
实际上再企业中使用的很少。
什么时候存在一对一?一个人一个身份证
package com.itheima12.hibernate.onetoone;
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;
public class OneToOneTest extends HibernateUtils{
@Test
public void testBuildR(){
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
//把sid为1和2的学生,把cid为1的班级提取出来
Student student1 = (Student)session.get(Student.class, 1L);
Student student2 = (Student)session.get(Student.class, 2L);
Classes classes = (Classes)session.get(Classes.class, 1L);
//建立关系
student1.setClasses(classes);
student2.setClasses(classes);
transaction.commit();
session.close();
}
}
龙哥上课课堂笔记:
外键一对多
现有person
再有address
这样才能进行关联
如果是使用中间表的时候
那么在address中就没有pi’d 了
通过中间表的话:可以不分先存谁 再 设置他们之间的关系。
单纯的存一个person对象没问题
将person对象屏蔽
再存一个address的话 也没有问题
之后如何建立关联呢 ?
判断对象的状态:根据持久化标识
在程序中 是不是设置了id
persion 和 address准备好
person中要求有一个集合 来建立之间的关系
set.add()_
p.setAddress(set)
尝试保存一下 person
当前 我这是new的对象
因为数据库中 已经有这个对象了,发insert语句
但是如果我设置了setid(1)
发现还是出错 因为 id=1 name =scotte 一摸一样的数据
地址也是游离态 现在save Person
1.save
2.中间表 当前的person和那个地址进行关联 说的没有id
有好多对象 都是框架给的 分不清不
单实例对象:同意一个对象 ge’t一个对象 所有人都操作同一个对象
多实例:操作不同的对象
所以 需要设置 address 的id 才能插入去
一但有了持久化标识 id 我就是托管态的对象
如果:
中间表 中加了唯一型约束:就变成一对多的关系 违反唯一性约束
如果中间表中 有何人的地址id=1 再存一个人 地址id=1 出差 报错为:有一条语句不能执行
如果 把pid=4的删除了,怎么办
先删除中间表
在删除peron
如果我删除的是1号地址而不删除person 会怎么样?
自己实验。
这个得看 会涉及到级联删除 和 控制反转
多对一
join 有个optionnal =true
通过 many-to-one 元素,可以定义一种常见的与另一个持久化类的关联。这种关系模型是多对一关联(实际上是一个对象引用-译注):这个表的一个外键引用目标表的主键字段。
基于中间表的多对一
中间表的 coulm 来 配置外键的关系
你得配置 唯一性约束 不然的话 就是多对多了
先设计表 再设计类 两张表之间通过外键建立练习
大多数情况下满足到二范式
三范式 就可以进行中间表的设计
多对多没办法 只能是中间表 为的是将来查数据库的时候 方便
单项和双向关联 :
单项:能够通过A表找到B表,通过B表找不到A表
双向关联的需求是:能够通过A表找到B表,通过B表找到A表。怎么实现呢?
我们可以知道,单存的B表数据找到的是一批A表的数据,也就是A表数据对象的集合,当然,改变的不是数据库中的表,而是持久化类。B表的持久化类中加入一个A表的Set集合,将查询到的与B有关的数据放入该集合中,之后就可以通过其他的条件得到我们想要的对应A的信息了。
总结:
单向和双向关联的区别就在于,两个类是否需要互相知道,如果类A需要知道类B,而类B也需要知道类A,那么这两个类就应该是双向关联的,而如果只需要类A知道类B,而不需要类B知道类A,那么就是单向关联。
做法:
1、B持久化类中饭加入A的set集合以及对应get set方法
2、在B表的映射文件中加入Set信息以及一对多信息:
这里写代码片
hibernate 希望你先建实体类 然后配置信息 再生成表
实际开发的时候:却是先建立表
得分清楚:
允许重复的是多
有单个对象的就是多
有set集合的是一
person{
id
name
aid
}
Address {
aid
addr
set<person> person
}
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" not-null="true"/>
</class>
<class name="Address">
//可以设置 非空约束
<id name="id" column="addressId" not-null="true"/>
<generator class="native"/>
</id>
//在address这头有个set集合
<set name="people" inverse="true">
//key 就是自己的id 即aid
<key column="addressId"/>
//绑定的class 是person类
<one-to-many class="Person"/>
</set>
</class
>
在见表语句中 我们可以发现:
外键约束 建立在person身上
单纯看表 是看不出双向关系的 关系维护是建立在person表中的
一个地址下面 对应多个person
再次强调:
用户 p1有个订单 101 ,101订单 对象商品明细(手机 冰箱 电视 扫地机器人 ),