Hibernate入门21 - 双向关联与inverse设定

本文介绍了Hibernate中User与Room的双向关联设计,给出了相关Java类和映射文件代码。还探讨了关联维护问题,未设置inverse时由Room维护关联存在性能问题,设置inverse=\true\将关联维护交给User可提高效率,但需明确设定双方参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

入门 21 - 双向关联与inverse设定

 之前我们对User与Room作了单向的多对一以及反过来的一对多关联,我们也可以让User与Room彼此参考,形成双向关联,就User与Room对象,具体来说,就是将程序如下设计:

User.java

package onlyfun.caterpillar;

 

public class User {

    private long id;

    private String name;

    private Room room;

  

    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 Room getRoom() {

        return room;

    }

    

    public void setRoom(Room room) {

        this.room = room;

    }    

}

 

Room.java

package onlyfun.caterpillar;

 

import java.util.*;

 

public class Room {

   private long id;

   private String address;

   private Set users = new HashSet();

  

   public long getId() {

       return id;

   }

   public void setId(long id) {

       this.id = id;

   }  

public String getAddress() {

     return address;

}

public void setAddress(String address) {

     this.address = address;

}

public Set getUsers() {

       return users;

}

   public void setUsers(Set users) {

       this.users = users;

   }

}


 而其对应的映射文件如下,首先是User.hbm.xml:

User.hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

 

<hibernate-mapping>

 

    <class name="onlyfun.caterpillar.User" table="USER">

 

        <id name="id" column="USER_ID" unsaved-value="0">

            <generator class="increment"/>

        </id>

 

        <property name="name">

            <column name="NAME" length="16" not-null="true"/>

        </property>

    

        <many-to-one name="room"

                     column="ROOM_ID"

                     class="onlyfun.caterpillar.Room"/>        

    </class>

 

</hibernate-mapping>


 再来是Room.hbm.xml:

Room.hbm.xml

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

 

<hibernate-mapping>

 

    <class name="onlyfun.caterpillar.Room" table="ROOM">

 

        <id name="id" column="ROOM_ID" unsaved-value="0">

            <generator class="increment"/>

        </id>

 

        <property name="address" type="string"/>

      

        <set name="users" table="USER" cascade="all">

            <key column="ROOM_ID"/>

            <one-to-many class="onlyfun.caterpillar.User"/>

        </set>

    </class>

 

</hibernate-mapping>


 这就形成了User与Room之间的双向关联映像,我们可以使用以下的程序进行测试:

HibernateTest.java

import onlyfun.caterpillar.*;

import net.sf.hibernate.*;

import net.sf.hibernate.cfg.*;

 

public class HibernateTest {

    public static void main(String[] args) throws HibernateException {

        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();

      

        Room room = new Room();

        room.setAddress("NTU-M8-419");      

      

        User user1 = new User();

        user1.setName("bush");      

      

        User user2 = new User();

        user2.setName("bush");

 

        /*

         *  因为没有设定inverser,所以只须从parent维护即可

         */

        //user1.setRoom(room);

        //user2.setRoom(room);

      

        room.getUsers().add(user1);

        room.getUsers().add(user2);

      

        Session session = sessionFactory.openSession();

        Transaction tx= session.beginTransaction();

        session.save(room);

 

        tx.commit();

        session.close();

 

        sessionFactory.close();

    }

}


 基本上就数据的储存来说,这样就已经足够,但这样的设计会有效能问题,显然的,这个程序将Room与User之间的关联交由Room来维持,就Room 而言,它要先储存自已,然后储存其所包括的多个User,之后再对每一个User更新(update)对自己(Room)的关联,具体而言,这个程序必须实行以下的SQL:

Hibernate: insert into ROOM (address, ROOM_ID) values (?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: update USER set ROOM_ID=? where USER_ID=?

Hibernate: update USER set ROOM_ID=? where USER_ID=?


 就Room而言,它并不知道其所包括的User是不是一个已储存的对象,或者即使为已储存对象,也不知道USER表格 上的ROOM_ID是不是参考至ROOM表格的ROOM_ID上,所以它必须针对自己所包括的User对象一个个进行更新,以确保USER表格上的 ROOM_ID是指向自己。
 如果将关联的维护交给User的话会比较容易,因为每个User都对应至一个Room,在储存时并用像Room一样必须对Set中的每个对象作检查,为 了将关联的维护交给User,我们可以在Room.hbm.xml中的<set>修改,加上inverse="true",表示将关联的维护「反过来」交给User作:

Room.java

<set name="users" table="USER" inverse="true" cascade="all">

            <key column="ROOM_ID"/>

            <one-to-many class="onlyfun.caterpillar.User"/>

        </set>


 由于将关联的维护交给User来作了,所以我们必须在储存时,明确的将Room设定给User,也就是说,必须这样作:

/*

 * 因为有user维护关联,所以必须呼叫setRoom

 */

user1.setRoom(room);

user2.setRoom(room);

      

room.getUsers().add(user1);

room.getUsers().add(user2);


 这比不加上inverse="true"设定时多了个指定的动作,您必须多键几个字,所带来的是效率上的增加,Hibernate的持久层管理员会先储存Room,然后储存User,如此就可以省去之前再进行更新的动作,具体来说,就是会执行以下的SQL:

Hibernate: insert into ROOM (address, ROOM_ID) values (?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)


 与先前不同的是,由于关联交给了User维护,所以这次Room不用一一更新USER以确定每个ROOM_ID都指向自已。
 如果指定了inverse="true",而不确实的将Room指定给User会如何?那么User与Room会各自储存,但彼此没有关联,也就是User将不会参考至Room,USER表格的ROOM_ID将为null,例如:

mysql> select * from USER;

+---------+------+---------+

| USER_ID | NAME | ROOM_ID |

+---------+------+---------+

|       1 | bush |    NULL |

+---------+------+---------+

 

mysql> select * from ROOM;

+---------+------------+

| ROOM_ID | address    |

+---------+------------+

|       1 | NTU-M8-419 |

+---------+------------+


 作个总结,在设立双向关联时,关联由多对一中「多」的哪一方维护,会比由「一」的哪一方维护来的方便,在Hibernate可以藉由inverse来设定,不设定inverse基本上也可以运行,但是效能会较差。
  设定了inverse,必须要明确的设定双方的参考,以这个主题的例子,Room要设定给User,而User也要知道Room的存在,这比不设定 inverse需要键入较多的字,但从另一方面,比较符 合程序设计的直觉(单看User与Room类别,两者要互相参考时,本来就要明确设定)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值