这篇文章主要探讨怎么用Spring来装配组件及其事务管理在JEE工程里连接到一个简单的数据库并不是什么难题但是如果要综合组装企业类的组件就变得复杂了一个简单的组件有一个或多个数据库支撑所以我们说到整合两个或多个的组件时我们希望能够维持跨组件的许多数据库的运作的原子性
JEE提供了这些组件的容器可以保证处理的原子性和独立性在没有JEE的情况下我们可以用SpringSpring基于IoC模式(即反转模式)不仅可以配置组件服务还可以配置相应的方法为了更好的实现本文的目的我们使用Hibernate来做相应的后台开发
装配组件事务
假设在组件库里我们已经有一个审核组件(audit component)里面有可以被客户端调用的方法接着当我们想要构建一个处理订单的体系我们发现设计需要的OrderListManager组件服务同样需要审核组件服务OrderListManager创建和管理订单每一个服务都含有自己的事务属性当这时调用审核组件就可以把OrderListManager的处理内容传给它也许将来新的业务服务(business service)同样需要审核组件那这时它调用的事务内容已经不一样了在网络上的结果就是虽然审核的功能保持不变但是可以和别的事件功能组合在一起用这些方法属性来提供不同的运行时的处理参数
在图中有两个分开的调用流程在流程里如果客户端含有一个TX内容OrderListManager 要由一个新的TX开始或者参与其中取决于客户端在不在TX里以及OrderListManager方法指定的TX属性这在它调用AuditManager方法的时候仍然适用
图 装配组件事务EJB体系通过装配者声明正确的事务属性来获得这种适应性我们不是在探讨是否声明事务管理因为这会使运行时的事务参数代码发生改变几乎所有的JEE工程提供了分布的事务管理来配合提交协议例如X/Open XA specification
现在的问题是我们能不能不用EJB来获得相同的功能?Spring是其中一种解决方案来看一下Spring如何处理这样的问题
用Spring来管理事务
我们将看到的是一个轻量级的事务机制实际上它可以管理组件层的事务集成Spring就是如此它的优点是我们可以不用捆绑在JEE的服务例如JNDI数据库最棒的是如果我们想把这个事务机制与已经存在的JEE框架组合在一起没有任何问题就好像我们找到了槓桿中完美的支撑点一样
Spring的另一个机制是使用了AOP框架这个框架使用了一个可以使用AOP的Spring bean factory在Spring特定的配置文件applicationContextxml里通过特定的组件层的事件来指定
<beans><! other code goes here >
<bean id=orderListManagerclass=orgspringframeworktransactioninterceptorTransactionProxyFactoryBean><property name=transactionManager>
<ref local=transactionManager/></property><property name=target>
<ref local=orderListManagerTarget/></property><property name=transactionAttributes>
<props><prop key=getAllOrderList>PROPAGATION_REQUIRED</prop>
<prop key=getOrderList>PROPAGATION_REQUIRED</prop><prop key=createOrderList>PROPAGATION_REQUIRED</prop>
<prop key=addLineItem> PROPAGATION_REQUIRED comexampleexceptionFacadeException</prop><prop key=getAllLineItems>PROPAGATION_REQUIREDreadOnly</prop>
<prop key=queryNumberOfLineItems>PROPAGATION_REQUIREDreadOnly</prop>
</props></property></bean></beans>
一旦我们在服务层指定了事务属性它们就被一个继承orgspringframeworktransactionPlatformTransactionManager 接口的类截获 这个接口如下:
public interface PlatformTransactionManager{TransactionStatus getTransaction(TransactionDefinition definition);
void commit(TransactionStatus status);
void rollback(TransactionStatus status);}
Hibernate事务管理
一旦我们决定了使用Hibernate作为ORM工具我们下一步要做的就是用Hibernate特定的事务管理实例来配置
<beans><! other code goes here ><bean id=transactionManagerclass=orgspringframeworkormhibernateHibernateTransactionManager>
<property name=sessionFactory>
<ref local=sessionFactory/>
</property></bean></beans>
我们来看看什么是装配组件事务你也许注意到了那个OrderListManager 特有的TX属性那个服务层的组件我们的工程的主要的东西在表的BDOM里:
educitycn/img_///gif >图 业务领域对象模型 (BDOM)为了用实例说明我们来列出工程里的非功能需求(NFR)
事务在数据库appfuse里保存
审核时要登入到另一个数据库appfuse里出于安全的考虑数据库有防火墙保护
事务组件可以重用
所有访问事件必须经过在事务服务层的审核
出于以上的考虑我们决定了OrderListManager 服务将委托任何审核记录来调用已有的AuditManager 组件这产生了表这样更细致的结构
educitycn/img_///gif >图 组件服务结构设计值得注意的是由于我们的NFR我们要映射OrderListManager相关的事物到appfuse 数据库里去而审核相关的到appfuse这样任何审核的时候 OrderListManager 组件都会调用AuditManager 组件我们认为OrderListManager 组件里的所有方法都要执行 因为我们通过服务来创建次序和具体项目那么AuditManager 组件里的服务呢? 因为它做的是审核的动作我们关心的是为系统里所有的事务记录审核情况这样的需求是即使事务事件失败了我们也要记录登录的审核情况AuditManager 组件同样要有自己的事件因为它同样与自己的数据库有关联如下所示
<beans><!—其他代码在这里><bean id=auditManagerclass=orgspringframeworktransactioninterceptorTransactionProxyFactoryBean><property name=transactionManager><ref local=transactionManager/></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 = orderListDAOcreateOrderList(orderList);
auditManagerlog(new AuditObject
(ORDER + orderId CREATE));
return orderId;}public void addLineItem (Long orderId LineItem lineItem)
throws FacadeException{
Long lineItemId = orderListDAO
addLineItem(orderId lineItem);
auditManagerlog(new AuditObject
(LINE_ITEM + lineItemId CREATE));
int numberOfLineItems = orderListDAO
queryNumberOfLineItems(orderId);
if(numberOfLineItems > ){
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调用了auditManagerlog() 方法你应该也注意到了上面方法中的事务属性
<bean id=orderListManager
class=orgspringframeworktransaction
interceptorTransactionProxyFactoryBean>
<pro