容器管理的分布式事务
分布式事务分类:
一个App对应若干DB
一个App对应1个DB,但两个app之间有事务
我们将第一幅图描述的情况姑且叫做1app2db,第二副图的情况叫做2app2db(这些仅仅是一个名称而已,不要误解)
我们下来分别描述一下每一种情况.
1app2db:
<jee:jndi-lookup id="dataSource_jndi_mysql" jndi-name="jdbc/mysql" cache="true" />
<jee:jndi-lookup id="dataSource_jndi_oracle" jndi-name="jdbc/oracle" cache="true" />
获取datasource(容其中定义的),由于对应多个db,故存在多个sessionFactory,多个datasource,etc.下面仅对一个进行描述,其余的配置是类似的.
<bean id="sessionFactory_mysql"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource_jndi_mysql" />
</property>
.......下略
定义mysql数据源的sessionFactory,oracle数据源的类似
<bean id="testDao" class="com.company.jncz.dao.HibernateBaseDao">
<property name="sessionFactory">
<ref local="sessionFactory_mysql" />
</property>
</bean>
<bean id="myService" class="com.company.jncz.MyService">
<property name="dao">
<ref local="testDao" />
</property>
</bean>
定义dao和service,oracle的类似
这样myService就可以使用了,而关键点是调用myService的EJB的方法必须要有事务定义.如:
public double businessAdd(String value) throws EJBException {
service.modifyUserName("4",value);//db1service2.modifyUserName("4",value);//db2
return 2.0;
}
businessAdd方法必须要被定义在ejb-jar.xml中,否则会报错,如下
java.sql.SQLException: SQL operations are not allowed with no global transaction by default for XA drivers. If the XA driver suppo
rts performing SQL operations with no global transaction, explicitly allow it by setting "SupportsLocalTransaction" JDBC connectio
n pool property to true. In this case, also remember to complete the local transaction before using the connection again for globa
l transaction, else a XAER_OUTSIDE XAException may result. To complete a local transaction, you can either set auto commit to true
or call Connection.commit() or Connection.rollback().
2app2db:
这种情况下呢,每一个app负责一个或若干个DB,而两个app之间有ejb调用.
关于dao,service的配置与上面相同,而ejb之间的调用,方法必须在ejb-jar.xml中定义事务,这样容器就能保证事务正确的传播和操作,比如一个ejb调用如下:
EJB_A.method(){
pojo.method();
EJB_B.method();
}
EJB_A.method()方法只要抛出异常,则事务会传播到EJB_B中,同样引起EJB_B回滚.
在做这个试验的时候,我采用的是一个weblogic跑两个domain的方法,遇到了一个问题,两个domain之间的ejb调用不能直接进行,必须要在域之间建立"信任"关系,具体做法如下:
mydomain->security->advanced->将"Enable Generated Credential"的勾去掉,同时在下面的框中输入密码,然后在另一个域中做同样的操作,注意两个域的密码要相同.另外域的信任关系是可以传播的,比如A信任b,c,那么b,c互相信任.
在使用容器管理的事务的时候,hibernate的 lazy问题必须解决,解决方法:采用拦截器,在方法开始之前打开session,之后关闭,详见另一片blog"优雅解决hibernate lazy问题".
______________________________________________________________________________________
其中配置的部分代码为:
<bean id="dataSource_jndi_mysql"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/mysql" />
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory_mysql"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource_jndi_mysql" />
</property>
<property name="mappingLocations">
<list>
<value>
classpath:/com/company/jncz/entity/User.hbm.xml
</value>
</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_outer_join">true</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.connection.pool_size">2</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.max_fetch_depth">1</prop>
<prop key="hibernate.query.factory_class">
org.hibernate.hql.classic.ClassicQueryTranslatorFactory
</prop>
</props>
</property>
</bean>
<bean id="testDao" class="com.company.jncz.dao.HibernateBaseDao">
<property name="sessionFactory">
<ref local="sessionFactory_mysql" />
</property>
</bean>
<bean id="myService" class="com.company.jncz.MyService">
<property name="dao">
<ref local="testDao" />
</property>
</bean>
<!-- jta start -->
<bean id="jndiTemplate"
class="org.springframework.jndi.JndiTemplate" singleton="true"
lazy-init="default" autowire="default" dependency-check="default">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">
weblogic.jndi.WLInitialContextFactory
</prop>
<prop key="java.naming.provider.url">
t3://127.0.0.1:7001
</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="jndiTemplate">
<ref local="jndiTemplate" />
</property>
<property name="userTransactionName">
<value>weblogic/transaction/UserTransaction</value>
</property>
<property name="transactionManagerName">
<value>javax.transaction.TransactionManager</value>
</property>
</bean>
<bean id="txProxyTemplate" abstract="true" lazy-init="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="business*">
PROPAGATION_REQUIRED,-TestException
</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="serviceManage" parent="txProxyTemplate">
<property name="target">
<ref local="myService" />
</property>
</bean>
</beans>