hibernate学习之第十篇

级联和关系维护
Cascade 用来说明当对主对象进行某种操作时,是否对其关联的从对象也作类似的操作,常用的cascade:none,all,save-update,delete,lock,refresh,evict,replicate,persist,merge,delete-orphan(one-to-many).一般对many-to-many,many-to-many不设置级联,在<one-to-one>和<one-to-one>中设置级联。

inverse表示“是否放弃维护关联关系 ”(在java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to-many的集合定义中使用,inverse=“true”表示该对象不维护关联关系;该属性的值一般在使用有序集合时设置成false(注意hibernate的缺省值是false )。
one-to-many维护关联关系就是更新外键。many-to-many维护关联关系就是在中间表增减记录。
注:配置成one-to-one的对象不维护关联关系。

接着上面的部门和员工的例子:
department的映射配置文件为:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate.collections">

        <class name="Department">
                <id name="id">
                        <generator class="native" />
                </id>
                <property name="name"/>
               
                <map name="emps">
                        <key column="depart_id" />
                        <map-key type="string" column="name"/>
                        <one-to-many class="Employee" />
                </map>
        </class>
</hibernate-mapping>
 

程序:

static void add(){
     Department depart = new Department();
        depart.setName("depart");

        Employee e1 = new Employee();
        e1.setName("e1");
        Employee e2 = new Employee();
        e2.setName("e2");

        Map<String, Employee> emps = new HashMap<String, Employee>();
        emps.put(e1.getName(), e1);
        emps.put(e2.getName(), e2);

        depart.setEmps(emps);
        Session s = HibernateUtil.getSession();
        Transaction tx = s.beginTransaction();
        //s.save(e1);
        //s.save(e2);
        s.save(depart);
        tx.commit();
}

 

执行以上代码,程序出现异常:严重: Could not synchronize database state with session
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: hibernate.collections.Employee

在代码中,department知道了employee.s1和s2未持久化,使得保存department时出现异常。 把配置文件做一下修改,
                <map name="emps" cascade="save-update" >
                        <key column="depart_id" />
                        <map-key type="string" column="name"/>
                        <one-to-many class="Employee" />
                </map>


再运行以上代码,就可以正确的保存s1和s2了,因为对他们进行了级联操作,保存department时,也save了employee。

级联操作,可以把上面的选项以逗号分隔开,逐一添加即可。如:
  <map name="emps" cascade="save-update,delete,refresh" ></map>

但级联操作也要慎用,要根据自己具体的业务需求来进行相关配置。

还有:
在修改department.hbm.xml配置文件:

        <map name="emps">
                        <key column="depart_id" />
                        <map-key type="string" column="name"/>
                        <one-to-many class="Employee" />
                </map>
 

执行以下代码:

 static void add() {
        Department depart = new Department();
        depart.setName("depart");

        Employee e1 = new Employee();
        e1.setName("e1");
        e1.setDepart(depart);
        Employee e2 = new Employee();
        e2.setName("e2");
        e2.setDepart(depart);

        Map<String, Employee> emps = new HashMap<String, Employee>();
        emps.put(e1.getName(), e1);
        emps.put(e2.getName(), e2);

        depart.setEmps(emps);
        Session s = HibernateUtil.getSession();
        Transaction tx = s.beginTransaction();
        s.save(e1);
        s.save(e2);
        s.save(depart);
        tx.commit();
    }

 

hibernate输出以下sql语句:

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set depart_id=?, name=? where id=?
Hibernate: update Employee set depart_id=?, name=? where id=?
 


上面的三条插入语句很容易理解。下面的四条更新语句分别对应:
 e1.setDepart(depart);
 e2.setDepart(depart);
 depart.setEmps(emps);
可以验证,如果把 e1.setDepart(depart);   e2.setDepart(depart);这两句注释掉,产生的更新语句只有两条:

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set depart_id=?, name=? where id=?
Hibernate: update Employee set depart_id=?, name=? where id=?
 



由此可见,hibernate根据程序代码的执行,采取相应的操作。其实更新语句只需做一次就够了 。对于数据库而言,在程序中,告诉部门有哪些员工,或者告诉员工,你属于那个部门。对于前者,部门需要维护关系。对于后者,员工需要维护关系。他们最终都可以在数据库表中表达出部门和员工的关系。在程序中对两者都手动设定了双方的关系,这样也可以,但显得有些冗余(做了两次更新)。因此,双方都会对关系进行维护,所以,双方都会向数据库提交设定关系的请求,这就带来了数据的重复设置,正如上面的四条更新语句。

解决问题的方案是:我们可以让其中一方放弃对关系的维护。 我们一般让一方放弃对关系的维护,让多方维护。这也比较好理解,正如现实生活中,老师不一定要记住所有学生,但学生记住老师则相对比较容易。
充:inverse不能在有序的集合中使用 。比如一般不这样配置:
<list name="emps" inverse="true"></list> 原因是,list需要维护添加的顺序,如果放弃了关系的维护,那么顺序就也不回得到hibernate的管理。这无疑是矛盾的。
只要在配置文件中做一下修改即可:
                <map name="emps" inverse="true">
                        <key column="depart_id" />
                        <map-key type="string" column="name"/>
                        <one-to-many class="Employee" />
                </map>
在执行以下代码:

Department depart = new Department();
        depart.setName("depart");

        Employee e1 = new Employee();
        e1.setName("e1");
        e1.setDepart(depart);
        Employee e2 = new Employee();
        e2.setName("e2");
        e2.setDepart(depart);

        Map<String, Employee> emps = new HashMap<String, Employee>();
        emps.put(e1.getName(), e1);
        emps.put(e2.getName(), e2);

        depart.setEmps(emps);
        Session s = HibernateUtil.getSession();
        Transaction tx = s.beginTransaction();
        s.save(e1);
        s.save(e2);
        s.save(depart);
        tx.commit();
 


执行的sql语句为:

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?
 


分析
:inverse="true"表示放弃对关系的维护。所以上面的两条更新语句是由Employee的setDepart方法产生的。因为关系有Employee来维护。由于department放弃了关系的维护,即使在代码中做了 depart.setEmps(emps)操作,hibernate也会忽略。通过以上方法,可以有效的减少数据库的操作次数,同时不影响关系的建立。

所谓的关系维护,就是更新外键。

改进:
从以上显示的sql语句,可以看出,在插入employee数据是也是插入了depart_id的,只不过是空值,然后插入department,这时就有了depart_id了,这时就去更新employee表,把depart_id更新。其实可以先保存department,这时一开始就有depart_id了,就不会有更新语句了。
代码如下:

 static void add() {
        Department depart = new Department();
        depart.setName("depart");

        Employee e1 = new Employee();
        e1.setName("e1");
        e1.setDepart(depart);
        Employee e2 = new Employee();
        e2.setName("e2");
        e2.setDepart(depart);

        Map<String, Employee> emps = new HashMap<String, Employee>();
        emps.put(e1.getName(), e1);
        emps.put(e2.getName(), e2);

        depart.setEmps(emps);
        Session s = HibernateUtil.getSession();
        Transaction tx = s.beginTransaction();
        s.save(depart);
        s.save(e1);
        s.save(e2);
        tx.commit();
    }

 

输出的sql语句为:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
 



心得: 根据程序执行过程中,打印输出的sql语句,只需冷静地分析程序的执行过程,不断地改进调优,可以有效的改善程序的性能。

你可能感兴趣的:(sql,Hibernate,生活,配置管理)