上一篇介紹了「多對一」(User → Room)。若 Room 也需要知道有哪些 User,即形成**一對多雙向關聯**。
<set name="users" table="user" cascade="all">
<key column="room_id"/>
<one-to-many class="com.hb3.pack_17.model.User"/>
</set>
此時 User.hbm.xml **無需任何映射**,User 不知道 Room 的存在。
Room room = new Room(); room.setAddress("NTU-M8-419");
room.setUsers(new HashSet<>());
room.addUser(new User("yangye"));
room.addUser(new User("shenbin"));
session.save(room); // cascade="all" 連帶存 User
User 新增 Room room 屬性;Room 保留 Set<User> users。
User.hbm.xml(多對一端):
<many-to-one name="room" column="room_id"
class="com.hb3.pack_20.model.Room"
cascade="save-update" outer-join="true"/>
Room.hbm.xml(一對多端):
<set name="users" table="user" cascade="save-update">
<key column="room_id"/>
<one-to-many class="com.hb3.pack_20.model.User"/>
</set>
Room room = new Room(); room.setAddress("NTU-M8-419");
room.setUsers(new HashSet<>());
room.addUser(user1); room.addUser(user2);
// User 端未 setRoom
session.save(room);
產生 SQL:
INSERT INTO room (address) VALUES (?)
INSERT INTO user (name, room_id) VALUES (?, ?) -- room_id 為 NULL!
INSERT INTO user (name, room_id) VALUES (?, ?) -- room_id 為 NULL!
UPDATE user SET room_id=? WHERE id=? -- 額外 UPDATE 回填
UPDATE user SET room_id=? WHERE id=?
因 Room 存入時尚未生成 ID,User 先存 room_id 為 NULL,事後再 UPDATE。**多出 2N 條 UPDATE**。
在 Room.hbm.xml 的 <set> 加上 inverse="true":
<set name="users" table="user" cascade="save-update" inverse="true"> ... </set>
並由 User 端 設定關聯:
User user1 = new User("bush");
User user2 = new User("caterpillar");
Room room = new Room("NTU-M8-419");
room.setUsers(new HashSet<>());
room.addUser(user1); room.addUser(user2);
// 關鍵:多方必須認識單方
user1.setRoom(room);
user2.setRoom(room);
session.save(room); // 或 session.save(user1), session.save(user2)
產生 SQL:
INSERT INTO room (address) VALUES (?)
INSERT INTO user (name, room_id) VALUES (?, ?) -- 一次到位,帶 room_id
INSERT INTO user (name, room_id) VALUES (?, ?)
口訣:「多對一雙向關聯,控制權給多的一方(inverse=true 在一的一方),多方負責 set 關聯。」
若 User 未 setRoom(room),資料庫 room_id 會是 NULL,等同於「員工沒分配部門」。務必在業務邏輯中保證雙向一致性。