CallBack在EJb3 spec中不过是一小章节,似乎不是很引人注意,刚开始,也没有引起我们的多少注意, 然而,在开发中,当我们实际运用它来解决问题的时候,发现它的实际作用还是很大的。 其实,从根本来说,CallBack相当于ORM Engine和用户设计的Domain Model(可持久话的模型) 之间一道天然的钩子,通过它,设计者可以放入很多类似横切面的关注点。 下面给出一个真实的例子:
银行需要对涉及帐务每一个操作实行录入,复核机制。假设操作涉及实体有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");
}
}
posted on 2007-07-30 11:20
冰封的爱 阅读(340)
评论(0) 编辑 收藏 所属分类:
J2EE