JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Object Relational Mapping » Hibernate  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 Hibernate 的 PO 直接付與 Business logic 合適嗎? [精華]
ingramchen

Web monkey



發文: 479
積分: 12
於 2004-04-18 13:23 user profilesend a private message to usersend email to ingramchenreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
最近 Hibernate 越玩越多, 開始出現了 design 的問題

PO 本身是對應 database 的 table ,它有點像 VO ,但其實又不是,任何人
new 一個 PO 之後,執行 session.save(PO) 便直接寫入 database 了。

假設有兩個 PO 一是 orderItem ,另一是 orderLog ,
當每次新增/修改一筆 order item 時,必定會有一 orderLog 產生

如果將 PO 當 VO 來用,那大概會用另一個 BO 全權去處理logic ,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Example A: PO as VO
// all attributes and constructor is public
public class OrderLog {
  private Long id ;
  private Long credit ;
  private OrderItem orderItem ;
  public Long getCredit() 
  public Long getId() {
  public void setCredit(Long long1) 
  public void setId(Long long1) 
  public OrderItem getOrderItem() 
  public void setOrderItem(OrderItem item) 
}
 
// OrderItem and OrderLog is bidirectional one-to-many association
public class OrderItem {
  private Long id ;
  private String name;
  private Set orderLogs;
  public void addOrderLog(OrderLog log) {
    log.setOrderItem(this);
    orderLogs.add(log);
  }
  public Long getId() 
  public String getName() 
  public Set getOrderLogs()
  public void setId(Long long1) 
  public void setName(String string) 
  public void setOrderLogs(Set set) 
}
 
// a BO to manage all PO, below is creating new orderItem
public class OrderManager {
  public void createOrderItem() {
    OrderItem item =  new OrderItem() ;
    item.setName("toy");
    OrderLog log = new OrderLog();
    log.setCredit(new Long(100));
    item.addOrderLog(log) ;
    session.save( item ) ;  // cascade save
  }
}
 


如果直接 PO 付與 Business logic 的話,變成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Example B: PO has Business logic
public class OrderItem {
  private Long id ;
  private String name;
  private Set orderLogs;
    // name and credit is always required (not-null)
  public OrderItem(String name, Long credit) {
    setName(name);
    setOrderLogs(new HashSet());
    // automatically generate one log when create a new order item.
    OrderLog log = new OrderLog(credit);
    addOrderLog(log);
  }
  // default constructor is private, this is for Hibernate
  private OrderItem() {
  }
  // all setter become private.
  private void setId(Long long1) 
  private void setName(String string) 
  private void setOrderLogs(Set set) 
  public void addOrderLog(OrderLog log) {
    log.setOrderItem(this);
    orderLogs.add(log);
  }
  public Long getId() 
  public String getName() 
  public Set getOrderLogs()
}
 
public class OrderLog {
  private Long id ;
  private Long credit ;
  private OrderItem orderItem ;
  //(1) constructor become package-default, only the same
  //    package can create a order log   
  //    (orderLog and orderItem are in the same package. )
  //(2) credit is always required for a log (not-null)
  OrderLog(Long credit) {
    set(credit);
  }
  // default constructor is private, this is for Hibernate
  private OrderLog() {
  }
  // all setter become private except setOrderItem()
  void setOrderItem(OrderItem item) 
  private void setCredit(Long long1) 
  private void setId(Long long1) 
  public OrderItem getOrderItem() 
  public Long getCredit() 
  public Long getId() {
}
 
// BO layer is now simplified, and it is error-proof !
public class OrderManager {
  public void createOrderItem() {
    // create item via constructor.
                OrderItem item =  new OrderItem("toy",new Long(100)) ;
    session.save( item ) ;  // cascade save
  }
}
 


Example A 讓所有的 PO 的 setter 全 public, business 全寫在 OrderManager
內。把 PO 當 VO 可以讓 BO 的撰寫更有彈性,而且集中管理。但是缺點是
下次有人 (可能是下個月後的自己) 又要動到 orderItem 了,如果忘了
setName(),或一筆 log,那........

Example B 做法是讓 PO 也有 business 意含, setter 全部 private,新增
一筆 Item 必定要包含 name 和 credit,而且自動產生一筆 log, 而 OrderLog
則整個鎖起來,只有同 package 的人可動。接下來的 BO layer 變的很簡潔,
而且OrderItem的使用者也不會再弄錯了。

example b 的作法看起來好像沒問題,但是想想這樣的作法跟 table 綁的死死
的,心裡毛毛的.... 請問有人試過這樣寫嗎?不知道未來會不會有問題.....

我大概了解 J2EE pattern 的建議,分一堆 DAO, business fasade, VO.....
讓 design 更有彈性,不過就我自已目前的經驗來看,越多 layer 只會搞死
自己而已..... 大多數的情況都是 over-design. 所以才會想拿 PO 來當一半的 BO


ingramchen edited on 2004-04-18 13:50
reply to postreply to post
作者 Re:Hibernate 的 PO 直接付與 Business logic 合適嗎? [Re:ingramchen]
koji

秒速5センチメートル

站長

發文: 8415
積分: 19
於 2004-04-19 10:54 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
這樣那如果資料庫有資料是必須update時不就不能透過setter
變成可能要先delete掉一筆資料在塞一筆資料才可以

koji


reply to postreply to post
作者 Re:Hibernate 的 PO 直接付與 Business logic 合適嗎? [Re:ingramchen]
chrischang





發文: 166
積分: 2
於 2004-04-19 21:24 user profilesend a private message to usersend email to chrischangreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
另外一個我覺得可行的方式就是當 new OrderItem default constructor 時, 也會新增一個 OrderLog, 當然這兩者會有設定好的 relationship, 重點在於當你去 set OrderItem.credit 時, 也去設定 OrderLog 的 credit, 這樣所有的 setter 都不需要變成 private, 一切如同 VO 一樣, 但是 setter method 上多作了一點小加工, 我想這個設計不會變化太大, hibernate 只要儲存 OrderItem, 也會順便儲存 OrderLog, 這其實跟 EJB entity bean 做法很像.

reply to postreply to post
作者 Re:Hibernate 的 PO 直接付與 Business logic 合適嗎? [Re:koji]
ingramchen

Web monkey



發文: 479
積分: 12
於 2004-04-19 21:53 user profilesend a private message to usersend email to ingramchenreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
koji wrote:
這樣那如果資料庫有資料是必須update時不就不能透過setter
變成可能要先delete掉一筆資料在塞一筆資料才可以

koji


我的作法是將 PO 視為一個完整功能的 BO,下面的 code
增加 OrderItem 的一個新欄位 qty,並增加修改的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Example C:  OrderItem 增加 qty.
public class OrderItem {
  private Long id ;
  private String name;
  private Set orderLogs;
  private Integer qty;
 
  // qty, name, and credit are required.
  public OrderItem(String name, Long credit, Integer newQty) {
    //constructor pre-condition check.
    preconditionCheck(credit.intValue(), newQty.intValue()) ;
 
    //add setQty here
    setQty(newQty);
    setName(name);
    setOrderLogs(new HashSet());
 
    // call helper to add log.    
    createLog(credit); 
  }
 
  // provide a new method for modify item's qty.
  public void modifyQty(int modifiedQty, int credit) {
     // method's precondition check
     preconditionCheck(modifiedQty, credit) ;
 
     setQty(modifiedQty) ;
 
     // automatic generate a log for this modification. 
     createLog(credit) ;
  }
 
  // log create helper
  private void createLog(int credit) {
    OrderLog log = new OrderLog(credit);
    addOrderLog(log);
  }
 
  // note that addOrderLog become private.
  // we don't outside of class to deal with log at all.
  private void addOrderLog(OrderLog log) {
    log.setOrderItem(this);
    orderLogs.add(log);
  }
 
  // precondition helper
  private void preconditionCheck(int qty, int credit) {
      if(qty < 0 || credit < 0) {
          throw new IllegleArgumentException("qty and credit must > 0");
      }
  }
 
  // default constructor is private, this is for Hibernate
  private OrderItem() {
  }
 
  // add getter setter for qty
  private void setQty(Integer i )
  public Integer getQty() 
 
  // all setter private.
  private void setId(Long long1) 
  private void setName(String string) 
  private void setOrderLogs(Set set) 
  public Long getId() 
  public String getName() 
  public Set getOrderLogs()
}
 


上面改變了原本 constructor 的 parameter, 而且新增了一個 modifyQty()
的 method. 這個 method 內部除了會修改 Qty 這個 field 之外,還會
做 pre-condition check, 以及新增一筆修改的 log.

這個 PO/BO 的獲得方式有兩種,一是直接透過 constructor. 只要
construtor 設計的好,就不怕其他 developer (一個月後的自己.....)
隨便就 new 一個 OrderItem,胡亂 set 那、這的,然後隨便就給你來
個 session.save(orderItem)....

另一獲得的方式是藉由Hibernate 由資料庫 load 出來。load 出來要改,也要
通過 public 的 business method 去更動。 error-proof~~~

依這樣的設計,OrderItem 這個 PO 就變成有 "行為",也有資訊隱藏。
只要這個 class 的 unit test 完整, OrderItem 的使用者不論是新增,
修改,這個 class 都會處理的好好的,所以才說 PO 當 BO 用......

還是回來標題所問的..... 不知這樣的設計.... 未來會不會爆掉?


ingramchen edited on 2004-04-19 22:14
reply to postreply to post
» JWorld@TW »  Object Relational Mapping » Hibernate

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8