现在我们来讨论一下一对多的单项关联。
首先把环境搭好。
我们就拿老生常谈的班级和学生作为例子。
关于班级类:有班级的id ,班级的名字,班级的描述。
关于学生类:有学生的id,学生的名字,学生的描述。
首先两张表要进行关联,首先需要外键,那么在一对多的情况中,外键是放在多的那一方。(外键在多的那一方是指生成的表在数据库中,外键在多的那一方)
首先我们要把2个类设计出来。
classes:
private Long cid;
private String cname;
private String cdescription;
//关于这里为什么用set,因为学生尽量不要重复,为了保证唯一性,所以用set
private Set<Student> students;
//在这里省略getter&setter
student:
private Long sid;
private String sname;
private String sdescription;
//同样在这里胜率setter&getter
然后就是配置文件,这里有2个domain,所以也要有2个映射文件/
首先是classes的
<class name="cn.ansel.domain.Classes">
<id name="cid" length="55">
<generator class="increment"></generator>
</id>
<property name="cname" length="55"></property>
<property name="cdescription" length="111"></property>
<!--
name:对于classes中的students
key:表示外键的名字
one-to-many:表示对应多的那一方的类的全名
-->
<set name="students">
<key column="cid"></key>
<one-to-many class="cn.ansel.domain.Student"/>
</set>
</class>
然后是student
<class name="cn.ansel.domain.Student">
<id name="sid" length="11">
<generator class="increment"></generator>
</id>
<property name="sname" length="11"></property>
<property name="sdescription" length="111"></property>
</class>
在Student类及配置文件中,都没有看到可以找到与classes关联的属性,这叫做单向关联,然后我们创建一个测试类,来生成表:
public class testCreateTable extends hibernateUtil {
@Test
public void createTable(){
}
}
最后,一定要记得,把2个映射文件都加到hibernate的配置文件中。
最后在数据库中,我们可以看到两张表,并且点开表之后,可以看到里面的属性,外键。
例子:
保存班级:
public void testSave_Classes(){
Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
Classes classes=new Classes();
classes.setCdescription("nice");
classes.setCname("rocket");
session.save(classes);
transaction.commit();
session.close();
}
运行结果如下图所示
保存学生:
public void testSave_Student(){
Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
Student student=new Student();
student.setSdescription("nice");
student.setSname("ansel");
session.save(student);
transaction.commit();
session.close();
}
运行结果如下图
在这里可以注意到,cid是没有值的!
保存学生和班级
@Test
public void testSaveClasses_Student(){
Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
//新建班级
Classes classes=new Classes();
classes.setCdescription("exciting");
classes.setCname("subway");
//新建学生
Student student=new Student();
student.setSdescription("so-so");
student.setSname("andy");
//新建存储学生的集合
Set<Student> students=new HashSet<Student>();
students.add(student);
//从班级中取出集合,并把创建的集合存到里面
classes.setStudents(students);
//分别保存
session.save(student);
session.save(classes);
transaction.commit();
session.close();
}
操作结果如下:
student表:
classes表:
可是这样保存两次,太麻烦,hibernate推出了级联操作,在保存一个有关联的对象的时候,跟这个对象关联的对象也会被保存。
级联操作:
在映射文件中的set属性,增加一个属性cascade
<!--
save-update:在保存关联的一方操作时,检查另外一方对象是否需要保存/更新
inverse:取值有true/false/default,其中true表示不维护该类与主键的关系,如果不维护,那么student表的cid的值会为null,要想该属性起作用,必须有2要求:
1、inverse的值为false/default
2、在客户端中必须声明关联类的关系,否则也是不起作用的
-->
<set name="students" cascade="save-update" inverse="false">
<key column="cid"></key>
<one-to-many class="cn.ansel.domain.Student"/>
</set>
那么在保存班级的同时,保存学生:
public void testCascade(){
Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
//新建班级
Classes classes=new Classes();
classes.setCdescription("healthy");
classes.setCname("PE");
//新建学生
Student student=new Student();
student.setSdescription("possitive");
student.setSname("jone");
//新建存储学生的集合
Set<Student> students=new HashSet<Student>();
students.add(student);
//从班级中取出集合,并把创建的集合存到里面
classes.setStudents(students);
//这里只保存了班级
session.save(classes);
transaction.commit();
session.close();
}
运行结果:
student表
classes表
这种操作又叫做隐式操作。
如果在映射文件中没有设置cascade属性,就会报错。
为什么会报这样的错误呢?
因为看我们的代码可以知道,我们只是保存了classes,但是此时,student还是处于瞬时状态的对象,由于没有设置级联的属性,所以就报对象参照了一个没有保存的瞬时对象的错误。