Struts2+EJB2+Spring2+Ibatis分布式事务参考
Spring+EJB
近日闲来无事,结合平时项目开发使用EJB2.x过程中带来的种种不便,决定结合Spring深入总结和研究下EJB开发和调用方面技术,主要包括三个方面:
1、事务控制
在Struts2+EJB2+Spring2+Ibatis的技术架构中,有三个地方可以进行事务控制
1)通过EJB2进行全局事务控制,EJB的事务控制分为两种:容器管理事务和Bean管理事务
使用EJB容器管理事务默认启用的是JTA事务,且是基于EJB方法的事务控制,要求在EJB方法包含完成一个业务且需要进行事务控制的各种操作,下边给出一个完整实例。
示例1(将事务完全交由ejb容器进行管理):
Spring的applicationContext.xml文件配置:
public class HelloBean implements javax.ejb.SessionBean { ... public String saveDM_DWLX(DM_DWLX param) { DM_DWLX temp = new DM_DWLX(); temp.setDWLX_DM("89"); temp.setDWLX_MC("政府机关"); boDM_DWLX.insertDM_DWLX(temp);//保证能够成功插入数据库 boDM_DWLX.insertDM_DWLX(param);//模拟插入失败情况 return null; } ... }
注意:这里的两次插入操作都是在ejb方法中进行的,如果将这两 次插入操作移植到boDM_DWLX.insertDM_DWLX(DM_DWLX param)方法中完成,那么事务就不能正常回滚了。因此,通过ejb控制事务时,ejb方法就变成了业务方法,封装了一个业务对应的各种操作,和项目技 术架构中使用ejb只作为facade和实现分布式的应用相违背,而且,每当业务发生变化都需要修改EJB,为系统修改造成极大的不方便。
ejb-jar.xml文件配置:
public static void main(String[] args) throws Exception { Properties p = new Properties(); p.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory"); p.put("java.naming.provider.url","t3://127.0.0.1:7001"); InitialContext ctx = new InitialContext(p); Object obj = ctx.lookup("Hello"); HelloHome home = (HelloHome)PortableRemoteObject.narrow(obj, HelloHome.class); Hello hello = home.create(); DM_DWLX model = new DM_DWLX(); model.setDWLX_DM("AAa");//模拟字段长度过长异常 model.setDWLX_MC("AA"); hello.saveDM_DWLX(model); }
测试结果:
在ejb方法saveDM_DWLX中,第一次插入操作成功,但第二次插入操作失败情况下,事务成功回滚
2)通过Spring2的声明式事务在业务层进行事务控制
Spring提供了两种事务管理方式,即编程式事务和声明式事务,这里主要记录使用声明式事务的配置
Spring声明式事务配置代码:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <!-- 配置远程JNDI数据源-->
- <bean id="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
- <property name="jndiName" value="ds" />
- <property name="jndiEnvironment">
- <props>
- <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
- <prop key="java.naming.provider.url">t3://127.0.0.1:7001</prop>
- <prop key="java.naming.security.principal">weblogic</prop>
- <prop key="java.naming.security.credentials">weblogic</prop>
- </props>
- </property>
- </bean>
- <!-- ibatis sqlMapClient -->
- <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
- <property name="configLocation" value="classpath:sql-map-config.xml"/>
- <property name="dataSource" ref="jndiDataSource"/>
- </bean>
- <!-- 事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="jndiDataSource"/>
- </bean>
- <!--jta事务管理器,需要进行全局事务时配置,使用jta事务,要求上面的jndi datasource配置成XA DataSource
- <bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
- </bean>
- -->
- <!--aop配置,这里主要设置对bo包下的类和业务方法进行事务控制-->
- <aop:config>
- <aop:pointcut id="defaultServiceOperation" expression="execution(* net.xp.service.bo..*.*(..))"/>
- <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
- </aop:config>
- <!--事务通知,针对不同方法配置事务属性-->
- <tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">-->
- <!-- <tx:advice id="defaultTxAdvice">
- <tx:attributes>
- <tx:method name="get*" read-only="true" rollback-for="Exception"/>
- <tx:method name="select*" read-only="true" rollback-for="Exception"/>
- <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="make*" propagation="REQUIRED" rollback-for="Exception"/>
- </tx:attributes>
- </tx:advice>
- <!-- 引入bo配置文件 -->
- <import resource="classpath:/config/dmtab.xml" />
- </beans>
使用该配置,事务将在bo层进行控制,符合在业务层进行事务控制的要求,且配置和应用简单,推荐使用该方案进行项目中的事务控制
3)通过IBatis对DAO层的事务进行控制
2、基于Spring的EJB开发
虽然Spring提供了很多帮助实现EJB的类,但实现起来那叫一个麻烦,引用Spring官方文档翻译后中的一段文字:
“与不使用Spring方式的EJB客户端相比,Spring的EJB客户端有一个额外的好处。通常如果要想能随意的在本地和远程EJB调用之间切换EJB客户端代码,是会产生问题的。这是因为远程接口的方法需要声明他们抛出的RemoteException
方 法,然后客户端代码必须处理这种异常,但是本地接口的方法却不需要这样。如果要把针对本地EJB的代码改为访问远程EJB,就需要修改客户端代码,增加处 理远程异常的代码,反之要么保留这些用不上的远程异常处理代码要么就需要进行修改以去除这些异常处理代码。使用Spring的远程EJB代理,我们就不再 需要在业务方法接口和EJB的实现代码中声明要抛出的RemoteException
,而是定义一个相似的远程接口,唯一不同就是它抛出的是RemoteException
, 然后交给代理对象去动态的协调这两个接口。也就是说,客户端代码不再需要与 RemoteException
这个checked exception打交道,实际上在EJB调用中被抛出的RemoteException
都将被
以unchecked exception RemoteAccessException
的方式重新抛出,它是RuntimeException
的一个子类。这样目标服务就可以在本地EJB或远程EJB(甚至POJO)之间随意地切换,客户端不需要关心甚至根本不会觉察到这种切换。当然,这些都是可选的,没有什么阻止你在你的业务接口中声明RemoteExceptions
异常。”
刚开始看到这段文字的确让我感到一种久违的兴奋,但经过编写代码实践却发现根本不是这么回事,实现本地EJB时客户端的确不需要捕获任何异常,但是 实现远程EJB时客户端仍然要捕获RemoteException,在官方文档中只提供了本地EJB调用的部分代码,并没有提供远程EJB调用的代码,突 然感觉有种被忽悠的感觉,在网上找了很多文章,只有问的,却没见到有回答的,真是让人不解~~
如果有哪位高人知道,可以给我回复,分享你的理解,在此先谢过
3、EJB本地接口和远程接口调用
EJB远程接口调用就不说了,主要说说EJB本地接口调用吧,以前在项目中都使用的是远程接口调用,从来没有使用本地接口调用,想着简单,就随便写了点代码试验一把,不试不知道,一试吓一跳,居然碰了一鼻子的灰,一直报NameNotFoundException。 最终将用于调用EJB本地接口的Servlet和ejb打包成EAR包部署后测试成功,一直不理解为什么将web和ejb分别以war和jar方式部署在 WebLogic的同一个domain下就不行了呢,呵呵,还得等待好心的高人给予解答~~