1、解析:html
一、 一对多双向关联也就是说,在加载班级时,可以知道这个班级全部的学生。java
同时,在加载学生时,也可以知道这个学生所在的班级。mysql
二、咱们知道,一对多关联映射和多对一关联映射是同样的,都是在多的一端加外键了。web
只不过是角度不一样,咱们从Classes一端来看,它就是一对多,它维护了一个一指向多的关系。在加载班级时,可以把学生加载上来。返过来,多个学生是属于一个班级的,它就是多对一。sql
三、而像咱们的用户和组,就是多对一。多对一维护了一种知道,就是多指向一,因此在加载学生时,就能拿到这个学生所在的班级。若是可以拿到它所在的班级,那么必须在对象模型Student中,持有Classes对象的一个引用。数据库
因此要在Student实体模型中,加入一个字段classes。这样才能实现双向的一对多的关联映射。session
2、新建hibernate_one2many_2项目app
3、修改对象模型 :测试
一、在Student.class中加上classes字段。ui
package com.bjsxt.hibernate;
public class Student {
private int id;
private String name;
Classesclasses;
public Classes getClasses() {
return classes;
}
public void setClasses(Classes classes) {
this.classes = classes;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
二、Classes类的内容不变
package com.bjsxt.hibernate;
import java.util.Set;
public class Classes {
private int id;
private String name;
private Set students;
public Set getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这样就实现双向了,Student类中有Class对象的引用。
一样,Classes类中有关Student对象的引用。
4、修映射文件。
一、修改改Student.hbm.xml映射文件
<?xmlversion="1.0"?>
<!DOCTYPE hibernate-mappingPUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<classname="com.bjsxt.hibernate.Student" table="t_student">
<idname="id">
<!--主键的生成方式不能是uuid。由于这种生成方式是生成32位16进制的字符串
而咱们的实体类中id的类型为int.因此要修改主键的生成方式为native.就是以数字形式自增-->
<generatorclass="native"></generator>
</id>
<propertyname="name"/>
<!--由于在Student端加了一个属性,因此在Student的映射文件里面要加上这个属性的映射
它的映射就是<many-to-one>
name就是Student类中的属性名classes
若是只指定属性名,而没有指定列,就会在咱们的表t_student中加入一个字段,名字是classes.
而且这个字段也指向一的一端。
但是在存储时,classes这段是不会存的。
缘由是t_classes表根本不知道这个字段的存在。
由于在Classes.hbm.xml映射文件中,根本就没有看到classes这个字段的影子。
若是像咱们的用户和组同样,在咱们的多的一端存,那么这个字段就能够存了,由于在
多的一端,也就是Student.hbm.xml文件中,配置了它。
就如用户和组,咱们new一个用户,而后new一个组,而后把这个用户设到这个组里,这样就存起来了。
可是,若是在多的一端存,则classesid字段就存不上了。由于在Student.hbm.xml文件中,
根本就没有classesid这个字段的关系。因此也就不知道classesid的存在。
因此,在映射时,映射的字段必须跟一对多那边的名字如出一辙。咱们在<many-to-oen>标签,用column属性来指定
因此说,在一对多双向关联映射中,在多的一端只有一个外键约束,就是跟在一的一端映射的列名相同的列名。
也就是说,从多的一端存,与在一的一端存,都会存到这个字段classesid上。
咱们说,many-to-one映射会加字段,但是在一对多双向关联映射中,这个关联的字段已经有了,因此就不用再加了。
-->
<many-to-onename="classes"column="classesid"/>
</class>
</hibernate-mapping>
二、 Classes.hbm.xml映射文件不变。
<?xmlversion="1.0"?>
<!DOCTYPE hibernate-mappingPUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mappingpackage="com.bjsxt.hibernate">
<class name="Classes" table="t_classes">
<idname="id">
<generatorclass="native"></generator>
</id>
<propertyname="name"/>
<!--上面为简单属性
下面要看一下集合要如何映射
答:集合要用set标签来映射
set标签的名字属性的值就是Classes类中的集合类型数据的变量名 -->
<setname="students">
<!-- 那么要如何映射set里面的东西呢?
咱们知道ont2many的映射关系,与many2one的映射是同样的,要在多的一端加字段的。
可是到目前为止,在t_student这张表里面是没有classid这个字段标识的。
因此要把这个字段加过来,加过来以后,还要作为外键指向t_classes这张表的主键
咱们用key标签来实现-->
<!--在key标签里面要使用的属性是列,就是给定在t_student表中的加上的列的名字。
加了key这个标签后,就会把classid这个字段加入到t_student这张表里面了,
它作为外键指向t_class表的id字段。-->
<keycolumn="classesid"></key>
<!-- 接下来,采用one-to-many这个标签,采用这个标签,一方面是一对多,
另外一方面,要用class这个属性来指定Classes.java类中的students这个集合里面
究竟是什么元素。咱们这个实例里面是Student对象集合。
必定要指定集合中的元素是什么类型的对象。-->
<one-to-manyclass="Student"/>
<!--ont-to-many标签就表明了students集合里面是Student对象。
这样指明以后,classes就能找到student这张表。
由于student在这里都写了。
并且在Student.hbm.xml文件中,也已经映射Student对象映射到哪张表了。
这样就能够把classid加到表t_student里面,并且作为外键指向t_classes表的主键id.-->
</set>
</class>
</hibernate-mapping>
5、数据库就不用建立了,用一对多单向关联那个数据库。
6、表也不用导出了,就用一对多单向关联那个数据库的表了。
7、测试存储
一、在多的一端存储
解析:在Student中,已经持有了Classes,而且它们之间的关系也已经配置了。
因此咱们如今能够new出classes来,而后调用student的setClasses()方法,将classes设进去就能够了。
public void testSave3(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();
Classes classes=new Classes();
classes.setName("理想Database实验室");
//瞬时对象要保存。否则在后面的代码中,保存Student时,会由于classs在数据库中没有而发生瞬时对象不存在异常
session.save(classes);
Student student1=new Student();
student1.setName("玉儿");
//给学生分配班级
student1.setClasses(classes);
session.save(student1);
Student student2=new Student();
student2.setName("黄根儿");
student2.setClasses(classes);
session.save(student2);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
执行后在显示SQL为:
先保存班级
insert into t_classes (name) values (?)
再存学生,由于学生知道本身是哪班的。由于先创建的班级。为何知道?由于先创建的关系。用student1.setClasses(classes)就知道这个学生所在的班级了,给它设上了,玉儿就是理想Database实验室的。
insert into t_student (name, classesid) values (?, ?)
insert into t_student (name, classesid) values (?, ?)
8、总结:因此采用一对多关系映射时,都在多的一端来维护。
一、第七个步骤是先存班级,再存学生。那么咱们像一对多单向关联映射那样,若是先存学生,再存班级,可不能够?固然仍是能够的。由于Class及Classes.hbm.xml文件没有改变呀。
因此若是执行testSave2()方法:
public void testSave2(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();
//先创建学生对象集合
Studentstudent1=new Student();
student1.setName("菜10");
session.save(student1);
Studentstudent2=new Student();
student2.setName("容祖儿");
session.save(student2);
//用泛型了
//由于班级里的学生是一个集合,因此要构造一个集合出来
Set<Student> students=newHashSet<Student>();
//向集合里面加数据
students.add(student1);
students.add(student2);
//要知道哪一个学生所在的班级,要new班级出来
Classesclasses=new Classes();
classes.setName("尚学堂");
//这个班级里面有哪些学生,要用set方法加上去。
//这样这个尚学堂班级里面就有两个学生了
classes.setStudents(students);
session.save(classes);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
执行后,数据也可保存上去。数据库中显示存入后的数据
mysql> select * from t_classes;
+----+--------------------+
| id |name |
+----+--------------------+
| 1 |尚学堂 |
| 2 | 理想Database实验室 |
| 3 |尚学堂 |
+----+--------------------+
3 rows in set (0.00 sec)
mysql> select * from t_student;
+----+--------+-----------+
| id | name | classesid |
+----+--------+-----------+
| 1 | 菜10 | 1 |
| 2 | 容祖儿| 1 |
| 3 | 玉儿 | 2 |
| 4 | 黄根儿| 2 |
| 5 | 菜10 | 3 |
| 6 | 容祖儿| 3 |
+----+--------+-----------+
6 rows in set (0.00 sec)
二、因此若是想让多的一端来维护,那么本来由一的一端来维护的,要让一的一端来维护的失消。
在Classes.hbm.xml文件中,默认反转为false。就是能够在一的一端来维护的。在此<setname=”students” inverse=”true”>
就是在set标签中设置反转为true。就是反转了,反给别人了,使一的一端失效。
三、这样设置完以后,若是再执行testSave2(),就是先存学生,此时学生的classesid字段的值为空。而后再存班级,而后由班级更新学生的classesid字段。这种反转是关系的反转。
执行后,在控制台产生三条insert语句,以下:
insert into t_student (name,classesid) value(?,?)当存学生时,classesid字段值确定为null.
insert into t_student (name,classesid) value(?,?)
insert into t_classes (name) value(?)
也就是说,t_classes表没有发出update语句来更新t_student表的classesid字段的值。由于咱们已经设置为反转了,不让一的一端来维护关系了,一的一端就无论了,因此update语句就不发了。虽然能够保存,可是在一的一端来维护的关系失效了,也就是说,班级根本不知道有学生存在。
MYSqL数据库显示以下:
mysql> select * from t_classes;
+----+--------------------+
| id |name |
+----+--------------------+
| 1 |尚学堂 |
| 2 | 理想Database实验室 |
| 3 |尚学堂 |
| 4 |尚学堂 |
+----+--------------------+
5 rows in set (0.00 sec)
mysql> select * from t_student;
+----+--------+-----------+
| id | name | classesid|
+----+--------+-----------+
| 1 | 菜10 | 1 |
| 2 | 容祖儿| 1 |
| 3 | 玉儿 | 2|
| 4 | 黄根儿| 2 |
| 5 | 菜10 | 3 |
| 6 | 容祖儿| 3 |
| 7 | 菜10 | NULL |
| 8 | 容祖儿| NULL |
+----+--------+-----------+
也就是说,执行结果以下:数据能够正常保存,可是关系字段为null.
9、因此当一的一端将关系设置为反转后,只能在多的一端来维护。
就是说,先new出classes,而后将其分配到学生里面。
在多的一端维护的好处是,不用发出update。由于学生知道本身是哪一个班级的了。
并且,为何在多的一端维护关系比较好?由于关系字段就在多的一端,就在student这个表里。固然谁离的近,谁维护好了。
10、readme. txt
hibernate 一对多关联映射双向。(双向关联Classed<----->Student)
一对多双向关联映射:
*在一一端的集合上使用<key>,在对方表中加入一个外键指向一一端
*在多一端采用<many-to-one>
注意:<key>标签指定的外键字段必须和<many-to-one>指定的外键字段一致,不然引用字段的错误
若是在“一”一端维护一对多关系, hibernate会发出多余的update语句,因此咱们通常在多的一端来维护关联关系
关于invserse属性:
inverse主要用在一对多和多对多双向关联上,inverse能够被设置到集合标签<set> 上,
默认inverse为false,因此咱们能够从“一”一端和“多”一端维护关联关系。
若是设置成inverse为true,则咱们只能从多一端来维护关联关系
注意:inverse属性,只影响数据的存储,也就是持久化
inverse和cascade
*inverse是关联关系的控制方向
*cascade操做上的连锁反应
十一
测试加载:加载学生,看一下能不能把学生对应的班级加载上来。
testLoad3()测试方法以下:
public void testLoad3(){
Session session=null;
try{
session=HibernateUtils.getSession();
Studentstudent=(Student)session.load(Student.class,4);
System.out.println(student.getName());
System.out.println(student.getClasses().getName());
session.beginTransaction();
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
生成的SQL语句:
select student0_.id as id1_0_, student0_.name as name1_0_,student0_.classesid as classesid1_0_ from t_student student0_ wherestudent0_.id=?
select classes0_.id as id0_0_, classes0_.name as name0_0_ fromt_classes classes0_ where classes0_.id=?
输出的数据:
黄根儿
理想Database实验室