Java, Spring Boot

JPA (Java Persistence API) Many-To-One & One-To-Many 在 Model 上的設置

57 / 100

Many-To-One & One-To-Many 在 Model 上的設置

  • Model

在系統的實務運作上,一個員工與主管,主管與部屬是很基本的架構,也常會放在同一個表格 ( Table ) 中。

jpa-model

  • 主管

基本的 Model 的設計應該是 @ManyToOne,若只加入主管時, 如程式碼14-17。

@Entity
@Table(schema = "SYS",name = "EMPLOYEE")
@Data
public class UserDetails implements java.io.Serializable {

 @Id
 @Column(name = "EMP_ID", unique = true, nullable = false, length = 30)
 private String empId;
 @Column(name = "EMP_JOB_TITLE", length = 12)
 private String empJobTitle;
 @Column(name = "MANAGER_ID", length = 30)
 private String managerId;

 @ManyToOne()
 @NotFound(action = NotFoundAction.IGNORE)
 @JoinColumn(name = "MANAGER_ID",insertable = false,updatable = false)
 private UserDetails manager;
}

要注意,若資料庫是 Oracle 時,@Table(schema = "SYS") 中的 schema 要加入,以免找不到表格 ( Table )

說明:

如果最上層主管(Root)的MANAGER_ID 是 NULL 時,可以加入 annotation optional=true

@ManyToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "MANAGER_ID",insertable = false,updatable = false)

如果最上層主管(Root)的MANAGER_ID,值不一定是 NULL,而是以 ROOT 或其它值代表為 Root 時,可以使用 @NotFound(action = NotFoundAction.IGNORE)

@ManyToOne()
@NotFound(action = NotFoundAction.IGNORE)
@JoinColumn(name = "MANAGER_ID",insertable = false,updatable = false)

這樣子就可以避免 NullPointerException 的錯誤

  • 部屬

部屬的資料也是常需要知道的,在 Model 中的設計則應該是@OneToMany

@ToString.Exclude    
@OneToMany(fetch = FetchType.LAZY ,mappedBy="manager")
private List<UserDetails> subordinates = new ArrayList<UserDetails>();

說明:

  1. @ToString.Exclude 可以避免 java.lang.StackOverflowError 的錯誤
  2. 在取部屬的資訊應使用 fetch = FetchType.LAZY ,並且在執行時應加入 @Transactional
  • 整個 Model 程式碼

@Entity
@Table(schema = "SYS",name = "EMPLOYEE")
@Data
public class UserDetails implements java.io.Serializable {

 @Id
 @Column(name = "EMP_ID", unique = true, nullable = false, length = 30)
 private String empId;
 @Column(name = "EMP_JOB_TITLE", length = 12)
 private String empJobTitle;
 @Column(name = "MANAGER_ID", length = 30)
 private String managerId;

 @ManyToOne()
 @NotFound(action = NotFoundAction.IGNORE)
 @JoinColumn(name = "MANAGER_ID",insertable = false,updatable = false)
 private UserDetails manager;
 
 @ToString.Exclude  
 @OneToMany(fetch = FetchType.LAZY ,mappedBy="manager")
 private List<UserDetails> subordinates = new ArrayList<UserDetails>(); 
}
  • 測試的程式

@SpringBootTest
@Transactional
public class BpmTest {

 @Autowired
 private UserDetailsRepository udRepo;
 
 @Test  
 public void basicTest() {
  Optional<UserDetails> ud = udRepo.findByEmpId("0001");
  if (ud.isPresent()) {
   System.out.println(ud.get().getEmpId()+":" +ud.get().getRealName());
   System.out.println("Boss:"+ud.get().getManager());
   
   List<UserDetails> lud = ud.get().getSubordinates();
   for (UserDetails uds : lud) {
    System.out.println("Member:"+ uds);
   }
   
  }
 }
}

 

  • 參考

Hibernate Self Join Annotations One To Many mapping example

Parent – Child relationship – self join mapping