您的位置:首页 > 其它

hibernate一对多关联映射——单向(继。空间不够了)(

2012-08-08 16:34 357 查看
十一、接着看存储与加载

建立单元测试类One2ManyTest

package com.bjsxt.hibernate;

import java.util.HashSet;

import java.util.Set;

import org.hibernate.Session;

import junit.framework.TestCase;

public class One2ManyTest extends TestCase {

}

十二、测试save(保存)

一对多的存储方式应该与多对一不一样。一对多存储的应该是classes。因为班级与学生的关系是在classes类里面的students集合。所以应该是先有学生,把学生建立好之后,再放到Classes类的students这个集合里面。

1、testSave()方法内容如下:

public void testSave(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();

Student student1=new Student();
student1.setName("菜10");
Student student2=new Student();
student2.setName("容祖儿");

Set<Student> students=new HashSet<Student>();
students.add(student1);
students.add(student2);

Classes classes=new Classes();
classes.setName("尚学堂");
classes.setStudents(students);
session.save(classes);

session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}

当testSave()方法执行时,会出现这个org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.bjsxt.hibernate.Student 异常。

2、正确的保存testSave2()

public void testSave2(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();

//先建立学生对象集合
Student student1=new Student();
student1.setName("菜10");
session.save(student1);
Student student2=new Student();
student2.setName("容祖儿");
session.save(student2);

//用泛型了
//因为班级里的学生是一个集合,所以要构造一个集合出来
Set<Student> students=new HashSet<Student>();
//向集合里面加数据
students.add(student1);
students.add(student2);

//要知道哪个学生所在的班级,要new 班级出来
Classes classes=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);
}
}

2.1执行后出现SQL语句:

insert into t_student (name) values (?)

只保存了学生的name,此时classesid字段值为null.

insert into t_student (name) values (?)

insert into t_classes (name) values (?)

hibernate又连接发了两条update,因为只有两个学生

update就是要把学生的classesid字段值更新上

根据学生id把classesid更新上。

也就是说,要由班级(一的一端)来维护这种关系:你菜10是我们班的,你容祖儿是我们班的。

在一的一端维护这种关系,会发现很多udpate。因为班级不知道会有多少学生。

就是说,由班级(一的端)主动记住这种关系

就如记人的名字一样,就如让姚明来记我们十三亿的人,这个关系要由姚来维护,

他要主动来记,把十三亿人的名字全部记下来。这肯定比较困难。因为由他一个人来更新,来记。

实际上,这种关系在多的一端来维护很容易,就如让我们记住姚明的名字很容易一样的。这是在一的端维护关系的一个缺点。另一个缺点是后面说的,如果在Student.hbm.xml映射文件中,将classesid字段设置为非空的话,则保存时会出错。

update t_student set classid=? where id=?

update t_student set classid=? where id=?

2.2执行结果数据库显示:

mysql> select * from t_classes;

+----+--------+

| id | name |

+----+--------+

| 1 | 尚学堂 |

+----+--------+

1 row in set (0.00 sec)

mysql> select * from t_student;

+----+--------+--------

| id | name |classesid

+----+--------+-------

| 1 | 菜10 | 1

+----+--------+-------

| 2 | 容祖儿 | 1

+----+--------+-------

1 row in set (0.00 sec)

2.3 我们现在分析一下数据库的结构,如这个测试方法,我们是先保存学生对象集合的。可是这时,这个学生对象里面只有名字与id,却没有classesid .可是表字段里是有classesid字段的。是在映射的时候加进去的。

可是如我们的测试方法,还是可以正确执行,班级里面可以加进去两个学生。 这是为什么呢?

原因是,我们的classesid字段是可以为空的。

也就是说,我们先把学生保存进去,可是这时 classesid 字段是没有值的。等到保存Classes时,再更新这个字段,将班级id值更新(update)上去。

如果我们在此Students.hbm.xml文件加上这样一条,让 classesid字段不能为空,再执行这个方法时,在存储时就会失败了,因为clssesid字段在保存时必须要值,不然就存不进去了。

<set name=”students”>

<key column=”classesid” not-null=”true”/>

<ont-to-many class=”com.bjsxt.hibernate.Student”/>

</set>

注:当修改完Students.hbm.xml文件时,要重新执行ExportDB.class文件,因为这里数据库中的表结构发生改变了。所以要重新生成表。

十三、测试加载

public void testLoad1(){
Session session=null;
try{
session=HibernateUtils.getSession();
Classes classes=(Classes)session.load(Classes.class,1);
Set students=classes.getStudents();

for
(Iterator iter=students.iterator();iter.hasNext();){
Student student=(Student)iter.next();
System.out.println("students.name="+student.getName());
}
session.getTransaction().commit();

}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}

public void testLoad2(){
Session session=null;
try{
session=HibernateUtils.getSession();
session.beginTransaction();
//注:load与get只能根据主键加载,根据别的字段是不可以的。
//第一个参数是相应的类
Classes classes=(Classes)session.load(Classes.class, 1);
System.out.println("classes.name="+classes.getName());
//拿出班级的学生,它是一个集合,然后一个一个输出学生名字。
Set<Student> students=classes.getStudents();

for
(Iterator<Student> iter=students.iterator();iter.hasNext();){
Student student=iter.next();
System.out.println("student.name="+student.getName());
}
session.getTransaction().rollback();
}catch(Exception e){
e.printStackTrace();
}finally{
HibernateUtils.closeSession(session);
}
}

执行后SQL语句为:

先根据id到班级里面来查询

select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?

班级查上来后,就可以把班级的学生查询上来。因为班级维护了一指向多的关系,所以它要找到这个关系字段,把班级的id拿来,然后它到t_student这张表中,找维护两个表关系的字段,就是classesid=这个班级的学生,把它一个一个的拿上来,它会自动地把这些学生放到学生集合里面。

select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_ from t_student students0_ where students0_.classesid=?

再回想一下一对多,查询用户时,也可以把组加上来。之所以可以这样,是因为我们配置了关系,如果没有在映射文件里面配置这种关系,则hibernate就不会给我们加上来了。

十四、readme.txt文件

hibernate 一对多关联映射。(单向日葵 Classed----->Student)
一对多关联映射利用了多对一关联映射原理。

多对一关联映射,在多的一端加入一个外键,指向一的一端。但是它维护的关系是多指向一的。
一对多关联映射,在多的一端加入一个外键,指向一的一端。但是它维护的关系是一指向多的。
也就是说,一对多与多对一映射策略是一样的,只不过站的角度不同。

因为是在一的一端维护的关系(为什么知道是在一的一端维护的关系呢?
因为我们将关系的配置写到一的一端就是Classes.hbm.xml文件里面了。
缺点:在一端维护关系的缺点:
*如果将t_student表里的classesid字段设置为非空,则无法保存
*因为在一的一端维护的,所以刚开始建立时,学生是不知道她是哪个班级的。
所以一的一端要发出多余的update语句。
因为不是在student这一端维护关系(就像我没有记住你的电话号,所以找不到你),
所以student不知道自己是哪个班的。所以需要发出多余的update语句来更新关系。

基于以上的缺点,所以一对多关联映射通常要做成双向的。就可以克服这些缺点了。双向的返过来就是多对一。
为什么要做成双向的,最大的考虑,是将关系交给多的一端来维护,不让一的一端来做了。
就像不能让姚明记住十三亿人的名字一样,我们记住姚的就可以了。

原文地址:http://blog.sina.com.cn/s/blog_5fad23090100fct8.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: