文/陶刚编译
设计:管理多个组件中的事务
现在我们讨论一下"集成组件事务"到底是什么意思。你可能注意到了,提供给OrderListManager(它是域中的一个服务层组件)的TX属性有所不同。图2显示了业务域对象模型(BDOM)中能够识别出来的主要对象:
图2:业务域对象模型(BDOM)
为了演示目的,我们列举了域中对象的一些非功能性的需求(NFR):
· 业务对象必须保存在数据库中appfuse1。 · 审计需要记录到一个独立的数据库appfuse2中,为了安全,它必须放在防火墙后面。 · 业务组件应该能重复使用。 · 必须审计业务服务层的每次尝试的所有活动。
考虑到上面一些需求,我们决定OrderListManager服务将代理任何对已有的AuditManager组件的审计日志调用。这就形成了图3所示的详细设计:
图3:组件服务的设计 这儿要重点注意的是,由于NFR(非功能性需求)的约束,我们把与OrderListManager(订单管理)相关的对象映射到了appfuse1数据库,把与Audit(审计)相关的对象映射到了appfuse2。接着,为了达到审计的目的,OrderListManager组件调用AuditManager组件。我们都认为OrderListManager组件中的方法是事务性的,因为我们使用这个服务来建立订单和条目。那么AuditManager组件中的服务是什么样的呢?由于我们认为AuditManager组件中的服务执行审计跟踪,因此我们对尽可能保持审计轨迹(即跟踪记录)很感兴趣,要保留系统中任何可能的业务行为的轨迹。这会引起另一种需求--即使主业务活动失败了,我们也需要建立一个审计条目。AuditManager组件也需要自己的事务,因为它也需要与自己的数据库交互操作。如下所示:
<beans>
<!-- other code goes here... --> <bean id="auditManager" class="org.springframework.transaction. interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="transactionManager2"/> </property> <property name="target"> <ref local="auditManagerTarget"/> </property> <property name="transactionAttributes"> <props> <prop key="log"> PROPAGATION_REQUIRES_NEW </prop> </props> </property> </bean>
</beans> | 现在我们把精力集中在两个业务服务上,也就是createOrderList和addLineItem。请注意,我们并没有采用最好的设计策略--你可能注意到了addLineItem方法抛出FacadeException异常,但是createOrderList却没有。在产品环境中,你可能希望每个服务方法都必须处理异常情况。
public class OrderListManagerImpl implements OrderListManager{
private AuditManager auditManager;
public Long createOrderList(OrderList orderList){ Long orderId = orderListDAO.createOrderList(orderList); auditManager.log(new AuditObject(ORDER + orderId, CREATE));
return orderId; }
public void addLineItem (Long orderId, LineItem lineItem) throws FacadeException{ Long lineItemId = orderListDAO.addLineItem(orderId, lineItem); auditManager.log(new AuditObject(LINE_ITEM + lineItemId, CREATE));
int numberOfLineItems = orderListDAO. queryNumberOfLineItems(orderId); if(numberOfLineItems > 2){ log("Added LineItem " + lineItemId + " to Order " + orderId + "But rolling back *** !"); throw new FacadeException("Make a new Order for this line item"); } else{ log("Added LineItem " + lineItemId + " to Order " + orderId + "."); } }
//其它代码... } | 为了演示的目的,我们建立了一个异常处理模块,引入了另外一条业务规则:一个订单不能包含两个以上订单项。我们现在应该注意到在createOrderList和addLineItem中对auditManager.log()方法的调用。你应该已经注意到为上面的方法提供的事务属性了。
<bean id="orderListManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionAttributes"> <props> <prop key="createOrderList"> PROPAGATION_REQUIRED </prop> <prop key="addLineItem"> PROPAGATION_REQUIRED,-com. example.exception.FacadeException </prop> </props> </property> </bean>
<bean id="auditManager" class="org. springframework.transaction.interceptor. TransactionProxyFactoryBean"> <property name="transactionAttributes"> <props> <prop key="log"> PROPAGATION_REQUIRES_NEW </prop> </props> </property> </bean> | PROPAGATION_REQUIRED等同于EJB中的TX_REQUIRED,PROPAGATION_REQUIRES_NEW等同于EJB中的TX_REQUIRES_NEW。如果我们希望服务方法一直在事务中运行,就可以使用PROPAGATION_REQUIRED。我们使用PROPAGATION_REQUIRED的时候,如果某个TX已经在运行中,那么bean方法加入那个TX,否则Spring轻量级TX管理器将为你重新启动一个。如果我们希望在组件服务被调用的时候,一般情况下启动新事务,那么就可以使用PROPAGATION_REQUIRES_NEW属性了。
我们还说明了,如果addLineItem方法产生FacadeException类型的异常,它就应该回滚(roll back)事务。这是另外一种粒度(granularity)层次,通过它我们可能细微地控制TX如何终止(即在碰到异常的情况下如何终止)。前缀-符号表明回滚TX,+符号表明提交TX。
下一个问题是,为什么我们给log方法赋予PROPAGATION_REQUIRES_NEW?这是我们的需求所驱动的:无论主服务方法发生了什么情况,我们都必须把系统中每次建立和添加订单项的尝试轨迹记录在审计中。这意味着,即使我们在createOrderList和addLineItem实现的内部遇到了任何异常情况,我们也得记录审计轨迹。只有我们启动新的TX并在新TX关系中调用log方法的情况下,这才是可行的。这就是为什么给log赋予了PROPAGATION_REQUIRES_NEW TX属性:如果我们调用auditManager.log(new AuditObject(LINE_ITEM + lineItemId, CREATE));
成功了,auditManager.log()就在新的TX上下文关系中发生,如果auditManager.log()自身是成功的(也就是没有产生异常),它就会被提交。
[上一页] [1] [2] [3] [4] [5] [下一页]
|