一、双向一对多
双向一对多==双向多对一,就是通过双方度能够找到对方的信息。
数据库关系:跟单向一对多和单向多对一是一样的。
实例
Student.java
package com.java.model;
public class Student {
private String name;
private long id;
//定义一个班级对象
private Classes c;
public Classes getC() {
return c;
}
public void setC(Classes c) {
this.c = c;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public String toString() {
return "Student [name=" + name + ", id=" + id + ", c=" + c + "]";
}
}
Student.hbm.xml
<?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>
<!--学生表class name对应类名,table对应数据库中的表名 -->
<class name="com.java.model.Student" table="t_student">
<!--主键id name对应实体类的属性,column对应数据库表中的字段 ,generator主键 -->
<id name="id" column="student_id">
<generator class="native"></generator>
</id>
<!-- 该对象的基本属性 -->
<property name="name" column="student_name">
</property>
<!-- 重点在这里
学生表外键和班级表主键相关联,
name:学生实体类中定义的那个对象的引用名称:private Class c;。
column:学生表中的外键名称。注意,是被外键约束的字段的名称,写这些配置文件,要时刻记得那两张数据库表的关系。
学生表外键名可以取和班级表主键名不一样的名字
本例学生表外键名:classId
本例班级表主键名:class_id
-->
<many-to-one name="c" column="classId" class="com.java.model.Classes" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
Classes.java
package com.java.model;
import java.util.HashSet;
import java.util.Set;
public class Classes {
private String name;
private long id;
private Set<Student> students=new HashSet<Student>();
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public String toString() {
return "Classes [name=" + name + ", id=" + id + "]";
}
}
Classes.hbm.xml
<?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>
<!--班级表class name对应类名,table对应数据库中的表名 -->
<class name="com.java.model.Classes" table="t_classes">
<!--主键id name对应实体类的属性,column对应数据库表中的字段 ,generator主键 -->
<id name="id" column="class_id">
<generator class="native"></generator>
</id>
<!-- 该对象的基本属性 -->
<property name="name" column="class_name">
</property>
<!--关键在这里,
name:set集合属性的名称(model里的classes类的学生集合名称)
key/column:这个是外键名,这个外键字段名不一定要和本类的主键字段名相同,这点要搞清楚,记住数据库表的关系,谁指向谁就不会混淆
这个外键值放在学生表里(不管是单向一对多还是单向多对一,都需要在多方加上外键)
one to many/class:一对多,所映射的类名(全限定类名,直接写类名也可以,hibernate会帮我们自动写好)
-->
<set name="students" cascade="save-update">
<key column="classId"></key>
<one-to-many class="com.java.model.Student" />
</set>
</class>
</hibernate-mapping>
导入映射文件hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--数据库连接设置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<!-- 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 控制台显示SQL -->
<property name="show_sql">true</property>
<!-- 自动更新表结构 -->
<property name="hbm2ddl.auto">update</property>
<!--导入映射文件-->
<mapping resource="com/java/model/Student.hbm.xml"/>
<mapping resource="com/java/model/Classes.hbm.xml"/>
</session-factory>
</hibernate-configuration>
测试类
package com.java.service;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.java.model.Classes;
import com.java.model.Student;
import com.java.util.HibernateUtil;
public class StudentTest2 {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public void testSaveClassAndStudentWithCascade() {
System.out.println("通过操作学生表进行级联保存");
//写好班级对象
Classes banji=new Classes();
banji.setName("15数媒");
//写好学生的对象
Student s1=new Student();
s1.setName("王五");
//将班级信息保存到学生对象中
s1.setC(banji);
Student s2=new Student();
s2.setName("赵六");
s2.setC(banji);
//持久化,只操作学生表,把学生信息放到学生表,班级表自动更新
session.save(s1);
session.save(s2);
}
@Test
public void testSaveClassAndStudentWithCascade2() {
System.out.println("通过操作班级表进行级联保存");
//写好班级信息对象
Classes c=new Classes();
c.setName("15软件");
//写好学生信息对象
Student s1=new Student();
s1.setName("张三");
Student s2=new Student();
s2.setName("李四");
//把学生对象加到班级对象
c.getStudents().add(s1);
c.getStudents().add(s2);
//将班级对象保存到班级表中,学生表自动更新
session.save(c);
}
}
结果
二、根据数据表调出数据
@Test
public void getStudentsByClass(){
System.out.println("从班级表中获取学生信息");
Classes c=(Classes)session.get(Classes.class, Long.valueOf(2));
Set<Student> students=c.getStudents();
Iterator it=students.iterator();
while(it.hasNext()){
Student s=(Student)it.next();
System.out.println(s);
}
}
@Test
public void getClassByStudents(){
System.out.println("从学生表中获取该学所在班级的信息");
Student s=(Student)session.get(Student.class, Long.valueOf(5));
Classes classes= s.getC();
System.out.println(classes);
}
三、关联维护
在多对多关联中,如果设置了inverse="true"就表示本方不进行关联的维护,由另一方进行关联的维护。
在一对多的关系中,一般有“多”方进行关联维护,所以Classes.hbm.xml设置inverse="true"(班级表不进行关联维护)
首先我们先写一个添加数据方法,但不添加他们的关系
@Test
public void testAdd(){
Classes c=new Classes();
c.setName("16信息与技术");
Student s1=new Student();
s1.setName("王五");
session.save(c);
session.save(s1);
}
外键为空,说明班级和学生没有联系
添加关联
@Test
public void testInverse(){
Classes c=(Classes)session.get(Classes.class, Long.valueOf(4));
Student s=(Student)session.get(Student.class, Long.valueOf(9));
//学生添加班级
s.setC(c);
//班级添加学生
c.getStudents().add(s);
}
添加完成但是我们发现写了两条sql语句
我们只需要在学生端维护主外键关系即可,inverse让某一端来维护主外键关系
Classes.hbm.xml
<?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>
<!--班级表class name对应类名,table对应数据库中的表名 -->
<class name="com.java.model.Classes" table="t_classes">
<!--主键id name对应实体类的属性,column对应数据库表中的字段 ,generator主键 -->
<id name="id" column="class_id">
<generator class="native"></generator>
</id>
<!-- 该对象的基本属性 -->
<property name="name" column="class_name">
</property>
<!--inverse="true" 表示本方classes不进行关联的维护,由另一方进行关联的维护。-->
<set name="students" cascade="save-update" inverse="true">
<key column="classId"></key>
<one-to-many class="com.java.model.Student" />
</set>
</class>
</hibernate-mapping>
把数据库数据删除,执行添加数据方法
添加关联方法
发现只执行了学生端s.setC(c);的sql语句,“多”的一段维护主外键关系设置成功
四、级联删除
先添加数据
我们想删除班级就可以删除完所有关联的学生,这就需要级联删除
首先先写一个删除方法
@Test
public void testDeleteClassCascade(){
Classes c=(Classes)session.get(Classes.class, Long.valueOf(5));
session.delete(c);
}
执行发现报错了,因为班级表关联着学生,所以不能删除班级
实现级联删除只需要在Classes.hbm.xml设置cascade="delete"(在班级端)
<?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>
<!--班级表class name对应类名,table对应数据库中的表名 -->
<class name="com.java.model.Classes" table="t_classes">
<!--主键id name对应实体类的属性,column对应数据库表中的字段 ,generator主键 -->
<id name="id" column="class_id">
<generator class="native"></generator>
</id>
<!-- 该对象的基本属性 -->
<property name="name" column="class_name">
</property>
<set name="students" cascade="delete" inverse="true">
<key column="classId"></key>
<one-to-many class="com.java.model.Student" />
</set>
</class>
</hibernate-mapping>
执行成功
五、删除学生
重新添加数据
执行删除学生方法
@Test
public void testDeleteStudent(){
Student s=(Student)session.get(Student.class, Long.valueOf(13));
session.delete(s);
}
说明学生端是可以直接删除数据,删除学生班级不会删除,班级端要删除数据,所有关联学生都要删除,所以只需要在Classes.hbm.xml设置cascade="delete"
六、一对多双向自身关联关系映射
自身是一也是多
Node.java
package com.java.model;
import java.util.HashSet;
import java.util.Set;
public class Node {
private long id;
private String name;
//父节点 ,自己是“多”的一方,设置一个类
private Node parentNode;
//子节点 ,集合,自己是“一”的一方,设置一个集合
private Set<Node> childNodes=new HashSet<Node>();
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 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;
}
}
Node.hbm.xml
<?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>
<class name="com.java.model.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.java.model.Node" cascade="save-update"></many-to-one>
<!--“一"方 ,一对多 -->
<set name="childNodes" inverse="true">
<key column="parentId"></key>
<one-to-many class="com.java.model.Node"/>
</set>
</class>
</hibernate-mapping>
hibernate.cfg.xml
<mapping resource="com/java/model/Node.hbm.xml"/>
测试NodeTest.java
package com.java.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.java.model.Node;
import com.java.util.HibernateUtil;
public class NodeTest {
private SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
private Session session;
@Before
public void setUp() throws Exception {
session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
}
@After
public void tearDown() throws Exception {
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
@Test
public 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);
}
}
结果截图