銀行需要對涉及帳務每一個操作實行錄入,復核機制。假設操作涉及實體有Customer,Account。 錄入紀錄實體為InputRecord, 復核紀錄為:ConfirmRecord.用戶為User。

@Entity class User{ @Id long id=-1; String userName; String passwordHex; } @Entity class Customer{ @Id long id=-1; String name; @OneToOne InputRecord inputRecord; @OneToOne ConfirmRecord confirmRecord; } @Entity class Account{ @Id long id=-1; String accountName; String accountNo; @OneToOne InputRecord inputRecord; @OneToOne ConfirmRecord confirmRecord; } //Not a persistence Entity abstract class OperationRecord{ @Id long id=-1; @ManyToOne User user; Date date; } @Entity class InputRecord extends OperationRecord{ //nothing ,just define a concrete class and table } @Entity class ConfirmRecord extends OperationRecord{ //nothing ,just define a concrete class and table }
1) OperationRecord沒有聲明為Entity,這里沒有用到繼承策略,因為對象繼承與數據庫之間的映射并不是很好,在一般設計中,我是很少使用它的,繼承和數據共享兩者是沒有什么關聯的, 因為在內存中,他們的實例始終是互補相干的數據副本,單純從數據來說,分別對應到不同的表中來存儲就是最好的映射,而行為的繼承在JVM中可以得到完全的體現。基于這個認識,InputRecord,ConfirmRecord僅僅起表明類型的作用。
2)從Account,Customer的代碼中可以看出InputRecord,ConfirmRecord域是重復出現的,而且從需求來看,會有更多的實體需要這兩個Field,從1)的分析中,很自然而然的抽取一個基類出來:
abstract class ConfirmableEntity{ @OneToOne InputRecord inputRecord; @OneToOne ConfirmRecord confirmRecord; } class Customer extends ConfirmableEntity{ }
到這里,一個對需求的思考同時也出現了,在什么地方紀錄操作記錄呢? AOP? TemplateMethod? 很多種可選的方案。然而,從代碼量和簡潔性來說,CallBack是好的。采用CallBack的基本骨干代碼如下:
abstract class ConfirmableEntity{ @PostPersist public void registerInputRecord(){ //retrieve current user User currentUser = UserHolder.getCurrentUser(); //create an inputRecord for current User InputRecord inputRec = new InputRecord(currentUser); setInputRecord(inputRec); } }
所有的需要錄入復核得實體只要繼承自這個ConfirmableEntity,不僅獲得數據,同樣獲得了相應的行為。這么一段代碼對于他們來說基本上時透明的。(有點類似AOP? )上面的代碼中,UserHolder是一個非常有意思的設計,用戶的保持一般有不同的需求,在Web中有Session,而在別的應用中就不一定使用這樣的機制了,但不管如何,我們總歸有在領域層提出獲取當前操作用戶的需求,一個很簡單的設計會讓很多事情變得簡單,可以把UserHolder當作一個隔離領域層和具體App層用戶管理的接口,大家如果對它得出現比較有詫異的話可以再具體討論一下.
值得一說的是,這樣的設計對于以后的可擴展也帶來了巨大的影響,比如,用戶提出需求:對于已經復核得操作紀錄不能再被修改,刪除。那么我們只要再在ConfirmableEntity上寫一個CallBack即可:
abstract class ConfirmableEntity{ @PostPersist public void registerInputRecord(){ //retrieve current user User currentUser = UserHolder.getCurrentUser(); //create an inputRecord for current User InputRecord inputRec = new InputRecord(currentUser); setInputRecord(inputRec); } @PreUpdate @PreRemove public void check(){ if(getConfirmRecord()!=null && getConfirmRecord().getId()>0 ) throw new SecurityException("can not update/remove confirmed record"); } }