|
|
在 多對一 與 一對多 中所實現的,分別是User對Room的單向關聯,以及Room對User的單向關聯。 在 一對多 中的儲存範例,有個效能的議題可以討論,若您使用Hibernate作為JPA的實作,當中的範例在儲存時,會產生以下的SQL語句: Hibernate:
insert into T_ROOM (address) values (?) Hibernate: insert into T_USER (age, name) values(?, ?) Hibernate: insert into T_USER (age, name) values(?, ?) Hibernate: update T_USER set ROOM_ID_FK=? where USER_ID=? Hibernate: update T_USER set ROOM_ID_FK=? where USER_ID=? 在儲存Room取得ROOM_ID之後,由於僅實現Room對User的一對多單向關聯,在儲存的時候,User無法直接參考到Room實例,所以只得先對User實例分別儲存,再將用UPDATE語句,以ROOM_ID更新T_USER表格的ROOM_ID_FK欄位。 若能實現一對多與多對一的雙向關聯,也就是User可以參考到Room,而Room也可以參考到User,在儲存時,可以將關聯維持的控制權交給多的一方,這樣會比較有效率,理由不難理解,就像是在公司中,老闆要記住多個員工的姓 名快,還是每一個員工都記得老闆的姓名快。 如果要實現User與Room的雙向關聯,則User可以如下設定:
package onlyfun.caterpillar; 而在Room這邊,注意使用mappedBy屬性來標示其為非主控方:
package onlyfun.caterpillar; 此時您可以如下以User為主控方進行儲存: Room room = new Room(); room.setAddress("NTU-M8-419"); User user1 = new User(); user1.setName("caterpillar"); user1.setAge(new Long(30)); user1.setRoom(room); User user2 = new User(); user2.setName("Justin"); user2.setAge(new Long(35)); user2.setRoom(room); EntityManager entityManager = JPAUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction etx = entityManager.getTransaction(); etx.begin(); entityManager.persist(user1); entityManager.persist(user2); etx.commit(); entityManager.close(); 若是要儲存Room,則可以設定User與Room交互參考,真正儲存時,直接儲存Room實例: Room room = new Room(); room.setAddress("NTU-M8-419"); room.setUsers(new HashSet<User>()); User user1 = new User(); user1.setName("caterpillar"); user1.setAge(new Long(30)); user1.setRoom(room); User user2 = new User(); user2.setName("Justin"); user2.setAge(new Long(35)); user2.setRoom(room); room.addUser(user1); room.addUser(user2); EntityManager entityManager = JPAUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction etx = entityManager.getTransaction(); etx.begin(); entityManager.persist(room); etx.commit(); entityManager.close(); 此時,JPA會將儲存的主控權轉為User,若使用Hibernate作為JPA的實作,則會產生以下的SQL語句,也就是不再需要額外用UPDATE來更新ROOM_ID_FK: Hibernate:
insert into T_ROOM (address) values (?) Hibernate: insert into T_USER (age, name, ROOM_ID_FK) values (?, ?, ?) Hibernate: insert into T_USER (age, name, ROOM_ID_FK) values (?, ?, ?) 類似的,一對一關係也可以藉由實例間互相參考設定為一對一雙向關聯,並於其中一方指定mappedBy屬性來設定其為非主控方,例如在 一對一(外鍵關聯) 的例子中,可以如下設定: ....
@Entity @Table(name="T_USER") public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="USER_ID") private Long id; private String name; private Long age; @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="USER_ROOM_ID", referencedColumnName="ROOM_ID") private Room room; .... } ....
@Entity @Table(name="T_ROOM") public class Room implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="ROOM_ID") private Long id; private String address; @OneToOne(cascade=CascadeType.ALL, mappedBy="room") private User user; .... } |