Hibernate双向多对一映射关系02双向级联,维护主外键关系

一、双向一对多

双向一对多==双向多对一,就是通过双方度能够找到对方的信息。

09e4c2f3a16e3198655f42f493ebf472e2e.jpg

 数据库关系:跟单向一对多和单向多对一是一样的。

a552f824c4e71ad94c46b8b874bd26bf54d.jpg

实例

cd97bc27a9d9cd7a2b58bb88d61fa49ef27.jpg

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);
	}
	

}

结果

d0f080f547265f9c17f1bb540405b1fb12a.jpgeee04703b389207832fbea7bcbea2983fc8.jpg

二、根据数据表调出数据

@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);
		
	}

c98e67f8223d372278da91b4f48b60a9c74.jpg086be76e871045fe80be9c7775fb4d05cb5.jpg

三、关联维护

在多对多关联中,如果设置了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);
	}

d67e5418012fb772bb919b1a21e45f90e10.jpgd398aef2c3f3c73e8284211962e058e6a13.jpg

外键为空,说明班级和学生没有联系

添加关联

@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);
	}

0d131eaae35c97958573eda94cd0de7600a.jpg3e03cd4056b741d609e256c01fdfe600d7f.jpg

添加完成但是我们发现写了两条sql语句

f123a68a4dcf244b99d904cc56ccade3c93.jpg

我们只需要在学生端维护主外键关系即可,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>

把数据库数据删除,执行添加数据方法

06114dfd7a580cc93c4323f3898f33ab4d9.jpg1599537a8131ee1eeb15da1fa024500512a.jpg

添加关联方法

89bd2a35af19a82cbfe756aa4b281be5611.jpg

发现只执行了学生端s.setC(c);的sql语句,“多”的一段维护主外键关系设置成功

a4d56082a51cd2f5fc8a38487ec685eb505.jpg

 

四、级联删除

先添加数据

eae16b0553cbb330f74ea9e7bda86262f2d.jpg8d241f33aa47eb4d66eba5dc3c525f9fbe2.jpg

我们想删除班级就可以删除完所有关联的学生,这就需要级联删除

首先先写一个删除方法

	@Test
	public void testDeleteClassCascade(){
		Classes c=(Classes)session.get(Classes.class, Long.valueOf(5));
		session.delete(c);
	}

执行发现报错了,因为班级表关联着学生,所以不能删除班级

b83df0724a8b4bf6f68718d14903d4cd691.jpg

实现级联删除只需要在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>

执行成功

6320bfb5d04af4357c7fd94150ef19c3338.jpg50981da1093ee111fe2c17ea9ac8dc2b534.jpg5e1ec3d454958312173ff0a3b4d2582e52d.jpg

五、删除学生

重新添加数据

69b9ea3c7de41e138f10cce60b0f932e932.jpg1ab7a6b6b5c74eae7e288181425a031f089.jpg

执行删除学生方法

	@Test
	public void testDeleteStudent(){
		Student s=(Student)session.get(Student.class, Long.valueOf(13));
		session.delete(s);
	}

说明学生端是可以直接删除数据,删除学生班级不会删除,班级端要删除数据,所有关联学生都要删除,所以只需要在Classes.hbm.xml设置cascade="delete"

6b7f79430ac9fcd874a8dda35ec4f753a97.jpg7c6e405ce06353670c36acf7f47ea9e76cd.jpg24fafa70d162dc7222962fbf36845d2ad4e.jpg

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

自身是一也是多

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);
	}
	
	
}

结果截图

84e04fc325daa9558f9aabf78ee3561eb73.jpg

转载于:https://my.oschina.net/u/3848699/blog/2223088

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值