一對多


接續 多對一 的內容,User對Room是多對一的關係,反過來Room對User就是一對多的關係,若要由Room來維持User的參考,則您可以如下設計:
  • 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.JoinColumn;
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)
@JoinColumn(name="ROOM_ID_FK")
private Set<User> users;

public Set<User> getUsers() {
return users;
}

public void setUsers(Set<User> users) {
this.users = users;
}


public void addUser(User user) {
users.add(user);
}

public void removeUser(User user) {
users.remove(user);
}

// 以下為 Getter、Setter
....
}

在這邊使用Set來維持對多個User實例的參考,而您的User可以如下設計:
  • User.java
package onlyfun.caterpillar;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
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;

// 以下為 Getter、Setter
....
}

一個儲存的例子如下所示,由於現在是由Room來維持對User的參考,所以直接儲存Room實例,User實例也會一併儲存:
        User user1 = new User();
        user1.setName("caterpillar");
        user1.setAge(new Long(30));

        User user2 = new User();
        user2.setName("Justin");
        user2.setAge(new Long(35));

        Room room = new Room();
        room.setUsers(new HashSet<User>());
        room.setAddress("NTU-M8-419");
        room.addUser(user1);
        room.addUser(user2);

        EntityManager entityManager =
                JPAUtil.getEntityManagerFactory().createEntityManager();
        EntityTransaction etx = entityManager.getTransaction();
        etx.begin();
        entityManager.persist(room);
        etx.commit();
        entityManager.close();

這個時候,有個效能上的議題可以探討,請參考 雙向關聯

在查詢時,要注意的是,@OneToMany預設的Fetch模式是FetchType.LAZY,若您直接以下面的程式打算顯示User資訊:
        EntityManager entityManager =
                JPAUtil.getEntityManagerFactory().createEntityManager();
        EntityTransaction etx = entityManager.getTransaction();
        etx.begin();
        room = entityManager.find(Room.class, new Long(1));
        etx.commit();
        entityManager.close();

        System.out.println(room.getUsers());

FetchType.LAZY時,除非真正要使用到該屬性的值,否則不會真正將資料從表格中載入物件,所以上例中,由於EntityManager已經關閉,而此時若要再載入User,就會發生例外錯誤,解決的方式之一是在EntityManager關閉前取得資料。
        EntityManager entityManager =
                JPAUtil.getEntityManagerFactory().createEntityManager();
        EntityTransaction etx = entityManager.getTransaction();
        etx.begin();
        room = entityManager.find(Room.class, new Long(1));
        etx.commit();

        System.out.println(room.getUsers());

        entityManager.close();

或者是在@OneToMany上指定fetch屬性為FetchType.EAGER,表示一併載入所有屬性所對應的資料:
    @OneToMany(cascade=CascadeType.ALL, mappedBy="room", fetch=FetchType.EAGER)
    private Set<User> users;

關於Fetch模式的說明,還可以參考 CascadeType 與 FetchType