hibernate3.2(八)一对多关联映射

在数据库设计方面,一对多关系关联和多对一关联关系是一样的,都是在多的一方维护关联关系,即在多的一方添加一个外键指向一的那一端(一般指向的是主键),我们不能在一的一方添加集合表示多的一方,这样也不符合数据库的设计范式。

 

数据库对应的实体类的关联关系的设计:

假设A关联B,A对B有依赖关系,就在A的一方添加对B的引用(即在A中添加一个关联字段关联B)。关联关系的命名一般都是把被依赖的一方放在后面表示。如A和B之间的关系是一对多的关联关系,那么就说A(一得一方)依赖了B(多的一方),要在A中添加关联字段关联B。一对多关联映射是在A中添加一个set集合关联B。

 

 一、单向一对多关联映射示例,班级关联学生:

public class Student {
	private int studentid;
	private String name;
	
                getter & setter...	
}

 

 

public class Classes {
	
	private int classid;
	private String name;
	private Set studets;//班级关联学生的关联关系属性
	
                getter & setter....
	
}

 Classes.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.wyx.hibernate">
	<class name="Classes" table="t_classes">
		<id name="classid">
			<generator class="native"/>
		</id>
		<property name="name"/>
		<set name="students">
			<key column="classid"/><!-- 创建一列classid字段加到student表中 -->
			<one-to-many class="Student"/><!-- 拿classid作student的外键 -->
		</set>
	</class>
</hibernate-mapping>

 

  

 Student.hbm.xml:

<?xml version="1.0"?>
<!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.wyx.hibernate.Student" table="t_student">
		<id name="studentid">
			<generator  class="native"/>
		</id>
		<property name="name"/>
	</class>
</hibernate-mapping>

测试save方法,从classes端保存: 

public void testSave(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			
			Student student1 = new Student();
			student1.setName("菜10");
			//session.save(student1);
			Student student2 = new Student();
			student2.setName("虫虫");
			//session.save(student2);
			

			Set students = new HashSet<Student>();
			students.add(student1);
			students.add(student2);
			
			Classes classes = new Classes();
			classes.setName("BDQN_T32");
			classes.setStudents(students);
			session.save(classes);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 

注释部分打开才不报错,控制台输出:

Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_student (name) values (?)
Hibernate: insert into t_classes (name) values (?)
Hibernate: update t_student set classid=? where studentid=?
Hibernate: update t_student set classid=? where studentid=?

可见原理是先在student表插入数据,但关联字段classid的值为null,因为此时student是被关联的一方,student不知道有class。class是一的一方,在这里是依赖方,需要维护表之间的关联关系,classes表插入数据时,就去修改student的classid,为student添加了关联管。

 

测试load方法,从clssses端取出student 的信息:

 

public void testSLoad(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			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("classes.student.name = " + student.getName());
			}
			System.out.println("");
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 控制台输出:

Hibernate: select classes0_.classid as classid0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.classid=?
classes.name = BDQN_T32
Hibernate: select students0_.classid as classid1_, students0_.studentid as studentid1_, students0_.studentid as studentid1_0_, students0_.name as name1_0_ from t_student students0_ where students0_.classid=?
classes.student.name = 菜10
classes.student.name = 虫虫

单向一对多映射的缺点:

1. 如果将student表的classid设置为非空限制,则无法保存。

2. 因为不是在sutdent端维护关系,所以student不知道是哪个classes的,所以需要发出多余的update语句更新关系。

所以单向一对多用的很少,最好设计成双向一对多的,把关系维护交给多的一方去处理会比较简单。

二、双向一对多关联映射示例,班级学生互相关联:

要使加载student也能够加载班级,所以在单向一对多关系映射的基础上,这样classes实体中有Set students,又在student实体中加入了一个classes字段,完成了双向关联。

 classes.hbm.xml不变,下面是修改过的student.hbm.xml:

<?xml version="1.0"?>
<!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.wyx.hibernate.Student" table="t_student">
		<id name="studentid">
			<generator  class="native"/>
		</id>
		<property name="name"/>
		<!-- column一定要和classes.hbm.xml中的key 下的 column一致,否则classid和classes字段都会生成 -->
		<many-to-one name="Classes" not-null="true" column="classid"/>
	</class>
</hibernate-mapping>

 测试save方法:

public void testSave(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			
			Classes classes = new Classes();
			classes.setName("BDQN_T32");
			session.save(classes);//transient状态如果不保存就用会报错
			
			Student student1 = new Student();
			student1.setName("菜10");
			student1.setClasses(classes);
			Student student2 = new Student();
			student2.setClasses(classes);
			student2.setName("虫虫");
			
			session.save(student1);
			session.save(student2);
			
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 打印结果:

Hibernate: insert into t_classes (name) values (?)
Hibernate: insert into t_student (name, classid) values (?, ?)
Hibernate: insert into t_student (name, classid) values (?, ?)

 

 测试load方法:

public void testLoad(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			Query query = session.createQuery("from Student");
			
			for(Iterator<Student> iter = query.iterate();iter.hasNext();){
				Student stu = iter.next();
				System.out.println("student.name = " + stu.getName() + " student.classes = " + stu.getClasses().getName());
			}
			Student student = (Student)session.load(Student.class, 1);
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 

打印输出:

Hibernate: select student0_.studentid as col_0_0_ from t_student student0_
Hibernate: select student0_.studentid as studentid1_0_, student0_.name as name1_0_, student0_.classid as classid1_0_ from t_student student0_ where student0_.studentid=?
Hibernate: select classes0_.classid as classid0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.classid=?
student.name = 菜10 student.classes = BDQN_T32
Hibernate: select student0_.studentid as studentid1_0_, student0_.name as name1_0_, student0_.classid as classid1_0_ from t_student student0_ where student0_.studentid=?
student.name = 虫虫 student.classes = BDQN_T32

 

总结:通常采用一对多关联映射,都是在多的一段维护关联关系,一的一端让他失效。只需要在classes.hbm的set集合里加入inverse="true"的属性(反转)。

<set name="students" inverse="true" cascade="all">
			<key column="classid"/><!-- 创建一列classid字段加到student表中 -->
			<one-to-many class="Student"/><!-- 拿classid作student的外键 -->
		</set>

 

 现在再从一的一方save一个classes,调用上面单向一对多关联映射的testSave方法,注释部分就不要打开了,不然保存classes的时候还是要发update,会修改student。

这里set标签下还有了cascade ="all" ,级联操作。得到的打印结果:

Hibernate: insert into t_classes (name) values (?)
Hibernate: insert into t_student (name, classid) values (?, ?)
Hibernate: insert into t_student (name, classid) values (?, ?)
这样不管从那边存,sql打印效果都一样了,hibernate为我们做了反转。

 

测试load,从classes中打印student的name:

public void testInverseLoad(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			Classes classes = (Classes)session.load(Classes.class, 2);
			for(Iterator<Student> iter = classes.getStudents().iterator();iter.hasNext();){
				Student stu = iter.next();
				System.out.println("classes.student.name = " + stu.getName());
			}
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 Hibernate: select classes0_.classid as classid0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.classid=?
Hibernate: select students0_.classid as classid1_, students0_.studentid as studentid1_, students0_.studentid as studentid1_0_, students0_.name as name1_0_, students0_.classid as classid1_0_ from t_student students0_ where students0_.classid=?
classes.student.name = liwei
classes.student.name = 张三1

 

测试load,从student中取classes的name:

public void testLoad1(){
		Session session = HibernateUtils.getSession();
		try {
			session.beginTransaction();
			Student stu = (Student)session.load(Student.class, 1);
			System.out.println("student.class.name = " + stu.getClasses().getName());
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally{
			HibernateUtils.closeSession(session);
		}
		
	}

 Hibernate: select student0_.studentid as studentid1_0_, student0_.name as name1_0_, student0_.classid as classid1_0_ from t_student student0_ where student0_.studentid=?
Hibernate: select classes0_.classid as classid0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.classid=?
student.class.name = BDQN_T33

你可能感兴趣的:(hibernate3)