hibernate中inverse理解与分析

来源:互联网 发布:深圳 人工智能 编辑:程序博客网 时间:2024/06/01 10:34

在hibernate中,inverse和cascade是比较容易混淆的两个概念,再利用百度搜索后发现大部分关于这两个概念的文章都是转载,对于我这种语文不好的人很难理解的。在综合多遍技术博客后,发现这两个是不同的概念,所以在讲解时,单独讲解inverse,不使用cascade,这样便于理解。


这里有两个表,年级与学生是一对多关系。

年级实体类:

public class Grade implements java.io.Serializable {private Integer gid;//主键private String name;private Set students = new HashSet(0);//省略set和get}

Grade.hbm.xml:

<hibernate-mapping>    <class name="edu.fjnu.po.Grade" table="grade">        <id name="gid" type="java.lang.Integer">            <column name="gid" />            <generator class="assigned" />        </id>        <property name="name" type="java.lang.String">            <column name="name" length="20" />        </property>        <set name="students" >            <key>                <column name="gid" />            </key>            <one-to-many class="edu.fjnu.po.Student" />        </set>    </class></hibernate-mapping>


学生实体类


public class Student implements java.io.Serializable {private Integer sid;private Grade grade;private String name;//省略set和get}


Student.hbm.xml:

<hibernate-mapping>    <class name="edu.fjnu.po.Student" table="student" >        <id name="sid" type="java.lang.Integer">            <column name="sid" />            <generator class="assigned" />        </id>        <many-to-one name="grade" class="edu.fjnu.po.Grade" >            <column name="gid" />        </many-to-one>        <property name="name" type="java.lang.String">            <column name="name" length="20" />        </property>    </class></hibernate-mapping>


以上为基本配置文件,没有使用inverse ,即默认inverse为false,意味着年级与学生都可以进行维护关系,我们分别从学生端和年纪端维护关系,看下运行后sql语句输出情况。

从年级(也就是一对多中“一”的那方)维护关系

<pre name="code" class="java">public class HibernateTest {public static void main(String[] args) {// 创建年级 大一Grade grade = new Grade();grade.setGid(1);// 设置年级主键grade.setName("大一");// 创建学生小王Student stu1 = new Student();stu1.setSid(1);// 设置学生stu1主键stu1.setName("小王");stu1.setGrade(grade);// 创建学生小陈Student stu2 = new Student();stu2.setSid(2);// 设置stu2学生主键stu2.setName("小陈");stu2.setGrade(grade);// 从年级端(也就是一对多中 一的那一端)维护两者关系Set<Student> student_set = new HashSet<Student>();student_set.add(stu1);student_set.add(stu2);grade.setStudents(student_set);// 准备进行储存Session session = HibernateSessionFactory.getSession();// 获取sessionTransaction tx = session.beginTransaction();// 创建事务session.save(grade);session.save(stu1);session.save(stu2);tx.commit();// 提交事务}}


我们会发现,除了正常的3条insert语句之外,会多出两条update语句。

我们看下这update语句,他是奖学生的外键又重新set了一遍,对于本程序,这个步骤完全是多余的。  难道hibernate有毛病吗?多出一些update语句使我们的运行效率降低,

而当同样的程序,我们在年级配置文件将inverse设为true ,会发现这两条update语句又消失了。所以说,这update语句是与inverse息息相关的。   

hibernate为什么要用inverse:

stu1.setGrade(grade);stu2.setGrade(grade);
上面程序中需要对学生设置年级,已确定数据库student表中 外键 gid和 年级中的主键 gid相同,正因为如此,hibernate“怕”在操作中可能会出现一些意外情况导致 外键不一致的情况(比如  stu1忘记setGrade ,即stu1.getGrade()=null),所以就出现了看似"多余"的语句,是由一对多的一那端去主动维护关系,保证多的那端 外键和一的那端主键相一致。

简单来说:hibernate怕出错, 所以就给你多执行一次无用的更新语句,以保证 年级中的学生都是必须和年纪相关联的。

为了证明这一观点 我们可以将 stu1 和stu2  的setGrade(grade)语句删除,我们会发现stu1 和stu2 依旧和年级成功的关联在一起,主键和外键相同,这就是这看似"多余"的update语句起了作用。保证我们的关联关系正常执行。

通俗易懂的讲,就是当这个学生没有年级时,大一年级主动去邀请这名学生加入这个年级。如果当有许多学生的话,这个邀请的过程效率就会非常低,所以我们一般在一对多的情况下,将 一 的那端设置inverse 为true,让多的那端去维护关系(即学生去主动找年纪)。

说到这里,我们就大概可以理解 为什么我们在class将inverse设为true后,update语句不会出现的原因,因为我们将维护关系交给student类,所以class类并不能主动去维护两者的关系。也就不会产生update语句。


从学生(也就是一对多中 多的那一方)维护关系

我们将"一"的那方inverse设为true后,维护工作就只能由“多”的那一方执行。

public class HibernateTest {public static void main(String[] args) {// 创建年级 大一Grade grade = new Grade();grade.setGid(1);// 设置年级主键grade.setName("大一");// 创建学生小王Student stu1 = new Student();stu1.setSid(1);// 设置学生stu1主键stu1.setName("小王");// 创建学生小陈Student stu2 = new Student();stu2.setSid(2);// 设置stu2学生主键stu2.setName("小陈");// 从一对多中多的这端(学生)这端维护双方关系stu1.setGrade(grade);stu2.setGrade(grade);/* * 注意:现在只能由多的那一方维护关系,所以以下代码是否添加都没有意义,因为学生不能维护两者关系,即对两者关系的操作都无效。 * Set<Student> student_set = new HashSet<Student>(); * student_set.add(stu1); student_set.add(stu2); * grade.setStudents(student_set); */// 准备进行储存Session session = HibernateSessionFactory.getSession();// 获取sessionTransaction tx = session.beginTransaction();// 创建事务session.save(grade);session.save(stu1);session.save(stu2);tx.commit();// 提交事务}

以上程序就是由 一对多中 '多"的一端维护双方关系,请大家注意注释中写的内容,因为现在只能由 多 的那端维护,所以在 "一"的那方对双方关系的操作都是不生效的。我们运行可以看到只有3条insert语句



通过这样,我们减少了不必要的update语句,从而提高了程序的运行效率。

总结:

许多文章仅仅粗略的告诉读者在一对多的情况下,将一中 inverse设为true 即能提高运行效率,看似适应绝大部分情况,但在我看来,只有懂得他的原理,在实际情况中根据实际问题分析,输出sql语句不断思考,才能将实现真正的优化。

许多读者分不清楚inverse与cascade,这里我贴一个外国技术讨论贴 ,个人认为讲的非常清楚。inverse与cascade区分  简单来说 cascade 只是帮助你去可以少写一部分的save或者update等语句,hibernate会自动判断和你相关联的类是否发生变化,如果发生变化会帮你save或update,这样你就可以少写部分代码,但实际上与hibernate的优化并没有太大关系。

ps:第一次写技术博客,如果写的不好,见谅。

1 0
原创粉丝点击