J2EE系列之Hibernate4学习笔记(五)--关联关系一对多映射

来源:互联网 发布:c语言单引号字符 编辑:程序博客网 时间:2024/06/05 20:13

上一篇博客实现了一对多单向实现,现在实现双向关联。

一、班级学生一对多映射实现(双向)

1.一个班级里面多个学生,在班级类中添加学生类集合属性:

package com.test.model;import java.util.HashSet;import java.util.Set;public class Class {private long id;private String name;private Set<Student> students = new HashSet<Student>();public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Set<Student> getStudents() {return students;}public void setStudents(Set<Student> students) {this.students = students;}}

2.更改Class类的映射文件:

<hibernate-mapping package="com.test.model"><class name="Class" table="t_class"><id name="id" column="classId"><generator class="native"></generator></id><property name="name" column="className"></property><set name="students" cascade="save-update"><key column="classId"></key> <!-- 对应的外键 --><one-to-many class="com.test.model.Student"/></set></class></hibernate-mapping>

这里使用<set>标签进行集合对象的标记,这里要注意设置cascade的值为save-update才能完成级联保存,通过保存班级信息级联完成学生信息的保存。里面<key>标签标记student类中的外键列,<one-to-many>标记级联的学生类。

3.新建测试类StudentTest:

package com.test.service;import java.util.Iterator;import java.util.Set;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.test.model.Class;import com.test.model.Student;import com.test.util.HibernateUtil;public class StudentTest {private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂private Session session;@Beforepublic void setUp() throws Exception {session=sessionFactory.openSession(); // 生成一个session    session.beginTransaction(); // 开启事务}@Afterpublic void tearDown() throws Exception {session.getTransaction().commit(); // 提交事务    session.close(); // 关闭session}@Testpublic void testClassAndStudentSave() {Class c = new Class();    c.setName("08数学");        Student s1 = new Student();    s1.setName("张三");        Student s2 = new Student();    s2.setName("李四");    c.getStudents().add(s1);    c.getStudents().add(s2);        session.save(c);}}


这个测试函数中学生对象s1、s2是临时对象,没有保存在session缓存中,只把新建的班级对象c保存在了session缓存中。运行测试函数发现班级信息以及学生信息都保存在了数据库中,这里级联保存发挥了作用。这个测试函数通过班级端保存了学生端的信息。


下面写一个测试函数,通过班级端来查找学生:

@Testpublic void getStudentsByClass(){Class c = (Class)session.get(Class.class, Long.valueOf(1));Set<Student> students = c.getStudents();Iterator<Student> it = students.iterator();while(it.hasNext()){Student s = (Student)it.next();System.out.println(s);}}

运行测试函数,控制台输出为:


这里查找到了该班级下面的所有学生信息。可以看到hibernate一共执行了两条sql语句,第一条是查找主键为1的班级信息,第二条这是查找该班级下面的学生信息。


这里就是双向实现的好处,可以从任意一端实现保存和查找另一端的信息。



二、inverse属性:它是用来指定关联的控制方的。inverse属性默认是false,若为false,则关联由自己控制,若为true,则关联由对方控制。

1.把之前的数据表t_student,t_class删除,写一个测试样例:

@Testpublic void testAdd(){Class c = new Class();c.setName("09数学");Student s1 = new Student();s1.setName("王五");session.save(c);session.save(s1);}

运行这个测试例子,在数据库中添加一条学生信息,一条班级信息:

    

此时的班级和学生是没有任何关系的。

2.写一个测试函数如下:

@Testpublic void testInverse(){Class c = (Class)session.get(Class.class, Long.valueOf(1));Student s = (Student)session.get(Student.class, Long.valueOf(1));s.setC(c);//c.getStudents().add(s);}

这里获取到班级类和学生类,下面的两条语句一个是学生类中设置班级信息,一个是班级信息中添加学生信息,这两条语句不论运行哪一条,由于实现了双向级联,结果都是一样的,都会把另一方的信息也保存在数据库中,都会把t_student表格中学生信息的classId一列赋值。也就是说不论运行哪一条,都会起到同时运行这两条语句的作用。也就是说班级和学生这两方都会维护数据库的级联关系。现在我们使用inverse属性,这个属性可以设置当前一方是否维护级联关系。修改class类的映射文件为:

<hibernate-mapping package="com.test.model"><class name="Class" table="t_class"><id name="id" column="classId"><generator class="native"></generator></id><property name="name" column="className"></property><set name="students" cascade="save-update" inverse="true"><key column="classId"></key> <!-- 对应的外键 --><one-to-many class="com.test.model.Student"/></set></class></hibernate-mapping>

这里设置了inverse属性为true,也就是权利翻转,即自己不再控制关联关系。进行了上述设置后,关联关系有Student类来维护,而Class类不再维护关联关系。


3.删除数据库中的表格,重新运行testAdd函数,此时班级信息和学生信息是没有关系的。运行testInverse函数:


可见,这里进行了级联关系,更新了t_student表中信息:



4.删除t_student、t_class表格,重新运行testAdd函数。修改testInverse并运行:

@Testpublic void testInverse(){Class c = (Class)session.get(Class.class, Long.valueOf(1));Student s = (Student)session.get(Student.class, Long.valueOf(1));//s.setC(c);c.getStudents().add(s);}

此时从班级端添加学生信息,由于此时已经设置了inverse属性为true,也就是班级端不再维护关联关系。这时运行结果为:




三、级联删除

这里要实现删除班级的时候把里面的班级信息一起删除。写测试函数:

@Testpublic void testDeleteClassCascade(){Class c = (Class)session.get(Class.class, Long.valueOf(1));session.delete(c);}

运行测试函数,程序运行失败,报错为有外键关联不能删除。这里class的主键是被student类的外键关联的,当hibernate检测到这个结果后是不能删除class类信息的。这里可以通过配置实现。修改class映射文件为:

<hibernate-mapping package="com.test.model"><class name="Class" table="t_class"><id name="id" column="classId"><generator class="native"></generator></id><property name="name" column="className"></property><set name="students" cascade="delete" inverse="true"><key column="classId"></key> <!-- 对应的外键 --><one-to-many class="com.test.model.Student"/></set></class></hibernate-mapping>

这里把cascade的属性值设置为了delete,再次运行程序成功实现了级联删除。


四、一对多双向自身关联关系映射

这里说一下什么是自身关联关系呢??举个例子,每个一级菜单可以有多个二级菜单,每个二级菜单又可以有多个三级菜单,这里菜单就是一个自身关联映射。每个二级菜单有一个父菜单,有多个子菜单。这里是一个一对多映射关系。废话少说,看下面的示例。

1.新建一个Node类:

package com.test.model;import java.util.HashSet;import java.util.Set;public class Node {private int id;private String name;private Node parentNode;private Set<Node> childNodes = new HashSet<Node>();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;}public Node getParentNode() {return parentNode;}public void setParentNode(Node parentNode) {this.parentNode = parentNode;}public Set<Node> getChildNodes() {return childNodes;}public void setChildNodes(Set<Node> childNodes) {this.childNodes = childNodes;}}

这里每个节点既是子节点又是父节点。


2.新建Node类的映射文件:

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.test.model"><class name="Node" table="t_node"><id name="id" column="nodeId"><generator class="native"></generator></id><property name="name" column="nodeName"></property><many-to-one name="parentNode" column="parentId" class="com.test.model.Node" cascade="save-update"></many-to-one><set name="childNodes" inverse="true"><key column="parentId"></key> <!-- 对应的外键 --><one-to-many class="com.test.model.Node"/></set></class></hibernate-mapping>

这里每个节点既是子节点又是父节点。

3.把这个映射文件添加到配置文件中。

4.新建一个Junit测试类:

package com.test.service;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.test.model.Node;import com.test.model.Student;import com.test.util.HibernateUtil;public class NodeTest {private SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂private Session session;@Beforepublic void setUp() throws Exception {session=sessionFactory.openSession(); // 生成一个session    session.beginTransaction(); // 开启事务}@Afterpublic void tearDown() throws Exception {session.getTransaction().commit(); // 提交事务    session.close(); // 关闭session}@Testpublic void testSaveMenu() {Node node = new Node();   node.setName("根节点");      Node subNode1 = new Node();   subNode1.setName("自节点1");      Node subNode2 = new Node();   subNode2.setName("自节点2");      subNode1.setParentNode(node);   subNode2.setParentNode(node);      session.save(subNode1);   session.save(subNode2);      }}

5.运行testSaveMenu()方法。看一下数据库中生成的结果为:


0 0
原创粉丝点击