在 多對一 與 一對多 中所實現的,分別是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=?
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可以如下設定:
- User.java
package onlyfun.caterpillar;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@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;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="ROOM_ID_FK")
private Room room;
// 以下為 Getter、Setter
....
}
而在Room這邊,注意使用mappedBy屬性來標示其為非主控方:
- Room.java
package onlyfun.caterpillar;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@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;
@OneToMany(cascade=CascadeType.ALL, mappedBy="room")
private Set<User> users;
...
}
此時您可以如下以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 (?, ?, ?)
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_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;
....
}
@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;
....
}