Hibernate关联映射+级联操作

Hibernate关联映射

简述:
    在关系型数据库中,通过外键来建立起表与表之间的关系,“一对多”关联等同于“多对一”关联,且两个表之间不存在多对多的关系,“多对多”关联需要通过中间表来实现,因此在关系型数据库中只有“一对一”、“一对多(多对一)”。而在Hibernate中通过关联映射把这种关联关系映射到了实体之中。关联映射用来处理表与表之间的关系,并且把表与表之间的主外键关系映射到实体之中。

关联关系分类:
                                   

   Hibernate中的关联关系分为以上七种,有些关系可以互相转换,例如:一对多映射等同于多对一映射,只是侧重点不同。

“一对多”:一个父亲可以有多个儿子,一个儿子只能有一个父亲,侧重点在父亲。

“多对一”:一个儿子只能有一个父亲,一个父亲可以有多个儿子,侧重点在儿子。

1.单向一对多、单向多对一

   单向一对多:关联关系只存在于“一”的一方,“多”的一方不存在关联关系

   单向多对一:关联关系只存在于“多”的一方,“一”的一方不存在关联关系

2.双向一对多、双向多对一(重点)

 “双向一对多”等同于“双向多对一”,也就是单向一对多与单向多对一的结合体。主要是在“一”的一方用一个集合来引用“多”的一方,“多”的一方用一个属性来引用“一”的一方。

此处主要用来区分inverse属性对cascade(级联)操作的影响。

以下是通过Hibernate反向工程生成的两个实体类:Student类与Classes类,Classes类作为“一”的一方,Student类作为“多”的一方。

   public class Classes {
	// Fields
	private Integer cid;
	private String cname;
 	private Set students = new HashSet(0);

           ......(省略get set方法)

   }   public class Student {
	// Fields
	private Integer sid;
	private Classes classes;
	private String sname;

            .......(省略get set方法)
   }

映射文件信息如下:

  <class name="com.aaa.entity.Classes" table="classes" catalog="project">
        <id name="cid" type="java.lang.Integer">
            <column name="cid" />
            <generator class="native" />
        </id>
        <property name="cname" type="java.lang.String">
            <column name="cname" />
        </property>
        <set name="students" inverse="true">
            <key>
                <column name="cid" />
            </key>
            <one-to-many class="com.aaa.entity.Student" />
        </set>
    </class>
<class name="com.aaa.entity.Student" table="student" catalog="project">
        <id name="sid" type="java.lang.Integer">
            <column name="sid" />
            <generator class="native" />
        </id>
        <many-to-one name="classes" class="com.aaa.entity.Classes"  fetch="select">
            <column name="cid" />
        </many-to-one>
        <property name="sname" type="java.lang.String">
            <column name="sname" />
        </property>
    </class>

     由以上映射文件可以看出:Classes类与Student类的映射文件信息中分别是通过<set>标签与<many-to-one>来维护关联关系。<set>标签中的inverse属性用来指明是否反转控制权,在不指明的情况下“一”的一方为主控方。<many-to-one>标签中的fetch用来指明加载策略。

级联添加:

inverse=true:“一”的一方最为被控方,“多”的一方作为主控方。(不指明inverse属性时,默认值为false)

添加Classes级联添加Student(不推荐使用)

将cascade="all"属性添加在Classes映射信息的set标签中,代表进行增删改操作时都级联,然后执行如下代码

 public static void main(String[] args) {//例一
	 Session se=HibernateSessionFactory.getSession();
		 Transaction tx=se.beginTransaction();
		     Classes cla=new Classes();
             cla.setCname("java2");
             //向班级添加学生
             Student stu=new Student();
             stu.setSname("张三");
             cla.getStudents().add(stu);
             se.save(cla); 
	     tx.commit();
	     se.close();
   }

执行结果:执行了两条insert语句,成功添加了两条记录,但是student表的外键为空。

原因是因为“被控方”也就是“一”的一方没有维护关联关系。既然关联关系不存在,那么外键也肯定就不存在。

添加Student级联添加Classes

将cascade="all"属性添加在student映射信息的<many-to-one>中,然后将上面的代码改为如下所示,然后执行:

   public static void main(String[] args) {//例二
	 Session se=HibernateSessionFactory.getSession();
		 Transaction tx=se.beginTransaction();
		     Classes cla=new Classes();
                      cla.setCname("java2");
                Student stu=new Student();
                stu.setSname("张三");
                stu.setClasses(cla);
                se.save(stu); 
	        tx.commit();
	        se.close();
}

执行结果:执行了两条insert语句,成功添加两条记录,并且成功添加外键关系。

inverse=false:“一”的一方为主控方,“多”的一方作为被控方。

修改inverse属性为false,将控制权交给Classes,然后分别执行例一、例二

例一执行结果:执行2条insert语句后执行一条update语句用来维护关系(将student中的外键信息更新)。

例二执行结果:稍微有点出乎意料,产生了一个 org.hibernate.TransientObjectException异常:产生这个异常的原因是因为Student引用了一个瞬时状态的对象,解决这个异常只需要在保存学生之前,将班级进行保存即可。

       例一的总结:进行级联添加,当被保存的对象(一)是主控方时,才会进行关系的维护,否者不维护关联关系。

       例二的总结:进行级联添加,当被保存的对象(多)是主控方时,可以级联添加,否者不能引用瞬时对象,也就是不能进行    级联操作。

级联更新:(不推荐使用,所以建议将cascade属性设置为save-update)

删除一的一方,同时更新多的一方(inverse=false

 public static void main(String[] args) {
	 Session se=HibernateSessionFactory.getSession();
		 Transaction tx=se.beginTransaction();
		 Classes cla=(Classes) se.get(Classes.class, 6);//6代表数据库中班级的ID
		 se.delete(cla);
	     tx.commit();
	     se.close();
}

     执行结果:Hibernate: update project.student set cid=null where cid=?

                    Hibernate: delete from project.classes where cid=?

从执行结果可以看出,先执行了update语句修改student表中的记录(将student中与之关联的外键设为null),然后再将此Classes删除。

然后将inverse属性设置为true,再次执行相同的操作。

    执行结果:Hibernate: delete from project.student where sid=?
                   Hibernate: delete from project.student where sid=?

                   Hibernate: delete from project.classes where cid=?

从执行结果可以看出,先执行了delete语句将student中与之关联的记录删除,然后再将此Classes删除。

    总结:被删除的对象(一)为主控方时,会先维护关联关系,将与之关联的外键设置为null,然后将自己删除。

              被删除的对象(一)为被控方时,不维护关联关系,会先将与之关联的对象删除,然后将自己删除。

              如果被删除的对象是“多”的一方,那么“一”的一方不受影响。

3.多对多

  。。。。此处省略一万字。

4.一对一

实现一对一关联有两种方式,分别是主键关联和唯一外键关联。

主键关联:两个对象使用一个相同的主键值,用来表名两者之间一一对应的关系,数据库中不会有额外的字段来维护关系,仅通过表的主键来关联。

唯一外键关联:在数据库中加入唯一外键约束,使外键的值不能重复,这样就通过一对多的方式来实现了一对一关联,映射时也是通过一对多的方式来映射。

总结:主键关联映射时,因为没有直接的关联关系,所以映射到java中的实体也不存在明显的关联关系。所以推荐使用唯一外键关联的方式来实现一对一。

     


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲得码黛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值