一.spring 3.0以前对Jtom的是默认支持的(需要spring的transaction系列包中的JotmFactoryBean对象),3.0以后不再供支持,解决方案:找到3.0以前的spring的transaction包将jta子包下JotmFactoryBean拷贝到当前项目中(需要反编译为.java文件)手动实现JotmFactoryBean,我测试的实现如下:
package com.dong.spring.jta;
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//package ;
import javax.naming.NamingException;
import javax.transaction.SystemException;
import org.objectweb.jotm.Current;
import org.objectweb.jotm.Jotm;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.transaction.jta.JtaTransactionManager;
/**
* {@link FactoryBean} that retrieves the JTA UserTransaction/TransactionManager
* for ObjectWeb's <a href="http://jotm.objectweb.org">JOTM</a>. Will retrieve
* an already active JOTM instance if found (e.g. if running in JOnAS),
* else create a new local JOTM instance.
*
* <p>With JOTM, the same object implements both the
* {@link javax.transaction.UserTransaction} and the
* {@link javax.transaction.TransactionManager} interface,
* as returned by this FactoryBean.
*
* <p>A local JOTM instance is well-suited for working in conjunction with
* ObjectWeb's <a href="http://xapool.experlog.com">XAPool</a>, e.g. with bean
* definitions like the following:
*
* <pre class="code">
* <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
*
* <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
* <property name="userTransaction" ref="jotm"/>
* </bean>
*
* <bean id="innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
* <property name="transactionManager" ref="jotm"/>
* <property name="driverName" value="..."/>
* <property name="url" value="..."/>
* <property name="user" value="..."/>
* <property name="password" value="..."/>
* </bean>
*
* <bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
* <property name="dataSource" ref="innerDataSource"/>
* <property name="user" value="..."/>
* <property name="password" value="..."/>
* <property name="maxSize" value="..."/>
* </bean></pre>
*
* Note that Spring's {@link JtaTransactionManager} will automatically detect
* that the passed-in UserTransaction reference also implements the
* TransactionManager interface. Hence, it is not necessary to specify a
* separate reference for JtaTransactionManager's "transactionManager" property.
*
* <p>Implementation note: This FactoryBean uses JOTM's static access method
* to obtain the JOTM {@link org.objectweb.jotm.Current} object, which
* implements both the UserTransaction and the TransactionManager interface,
* as mentioned above.
*
* @author Juergen Hoeller
* @since 21.01.2004
* @see JtaTransactionManager#setUserTransaction
* @see JtaTransactionManager#setTransactionManager
* @see org.objectweb.jotm.Current
*/
public class JotmFactoryBean implements FactoryBean, DisposableBean {
private Current jotmCurrent;
private Jotm jotm;
public JotmFactoryBean() throws NamingException {
// Check for already active JOTM instance.
this.jotmCurrent = Current.getCurrent();
// If none found, create new local JOTM instance.
if (this.jotmCurrent == null) {
// Only for use within the current Spring context:
// local, not bound to registry.
this.jotm = new Jotm(true, false);
this.jotmCurrent = Current.getCurrent();
}
}
/**
* Set the default transaction timeout for the JOTM instance.
* <p>Should only be called for a local JOTM instance,
* not when accessing an existing (shared) JOTM instance.
*/
public void setDefaultTimeout(int defaultTimeout) {
this.jotmCurrent.setDefaultTimeout(defaultTimeout);
// The following is a JOTM oddity: should be used for demarcation transaction only,
// but is required here in order to actually get rid of JOTM's default (60 seconds).
try {
this.jotmCurrent.setTransactionTimeout(defaultTimeout);
}
catch (SystemException ex) {
// should never happen
}
}
/**
* Return the JOTM instance created by this factory bean, if any.
* Will be <code>null</code> if an already active JOTM instance is used.
* <p>Application code should never need to access this.
*/
public Jotm getJotm() {
return this.jotm;
}
public Object getObject() {
return this.jotmCurrent;
}
public Class getObjectType() {
return this.jotmCurrent.getClass();
}
public boolean isSingleton() {
return true;
}
/**
* Stop the local JOTM instance, if created by this FactoryBean.
*/
public void destroy() {
if (this.jotm != null) {
this.jotm.stop();
}
}
}
2.Spring整合Jtom的需要的文件配置:
(1).主配置 spring_jtom.xml
=====================
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="jotm" class="com.dong.spring.jta.JotmFactoryBean"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
</bean>
<bean id="dts1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/jtom_t1" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="mysql" />
</bean>
<bean id="dts2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/jtom_t2" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="mysql" />
</bean>
<bean id="template1" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dts1" />
</bean>
<bean id="template2" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dts2" />
</bean>
<import resource="annotion_bean.xml"/>
</beans>
=============================
说明:以上配置部分主题上分 四大部分,1-Jtom管理事务所需的JtomBean(配置文件中为"com.dong.spring.jta.JotmFactoryBean的实例jtom),2-事务管理的manager对象(配置文件中为:org.springframework.transaction.jta.JtaTransactionManager的实例transactionManager),3-数据源DataSource对象(如org.enhydra.jdbc.standard.StandardXADataSource的两个实例dts1和dts2,要实现分布式事务至少要两个数据源代表来自两个不同的数据库的链接,这里用的是一个MSQL数据库因为是模拟实现(模拟两个库jtom_t1,jtom_t2),实际中dts1与dts2应是不同数据库的数据源),4-实现领域对象的CRUD操作的Template对象(org.springframework.jdbc.core.JdbcTemplate的实例template1,template2,这里两个template对象就不解释了);
(2).基于AOP的切面拦截管理事务配置
annotion_bean.xml文件
===================================
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.dong.dao"/>
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* com.dong.dao.UserDao.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" rollback-for="Exception" />
<tx:method name="insert*" rollback-for="Exception" />
<tx:method name="save*" rollback-for="Exception" />
<tx:method name="update*" rollback-for="Exception" />
<tx:method name="del*" rollback-for="Exception" />
<tx:method name="remove*" rollback-for="Exception" />
<tx:method name="*" read-only="true" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
</beans>
说明:以上基于切面事务管理的部分分为两个部分,1-aop切点serviceMethods(ID为serviceMethods的对象),2-事务切面策略txAdvice(ID为txAdvice的对象);注意点:在serviceMethods切点中要指明切面策略txAdvice(配置中:advice-ref="txAdvice" ),在切面策略txAdvice中要指定事务管理对象txManager(配置中:transaction-manager="txManager");主题关于AOP的事务管理配置就不多做解释了;还有一个配置项“ <context:component-scan base-package="com.dong.dao"/>” 这个配置指明要spring扫描com.dong.dao包下的自动装配对象,在我的测试中是UserDao对象,这个对象很关键,实现对象的CRUD操作考的就是它,还有需要注意的一点就是serviceMethods切点的表达式:"execution(* com.dong.dao.UserDao.*(..))“,其实就是指明要拦截的事务操作的方法就是UserDao对象的方法;
3.实行事务操作的Dao对象代码如下:
package com.dong.dao;
import java.sql.Types;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.dong.model.User;
@Component(value="userDao")
public class UserDao {
@Resource(name="template1")
JdbcTemplate temp1;
@Resource(name="template2")
JdbcTemplate temp2;
public void add(User user){
String sql="insert into user (id,name,age,sex,address) values(?,?,?,?,?)";
temp2.update(sql,new Object[]{user.getId(),user.getName(),user.getAge(),user.getSex(),user.getAddress()},new int[]{Types.BIGINT,Types.BIGINT,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR});
temp1.update(sql,new Object[]{user.getId(),user.getName(),user.getAge(),user.getSex(),user.getAddress()},new int[]{Types.BIGINT,Types.BIGINT,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR});
}
}
说明:Dao对象实现对user对象的CURD操作这里只写了一个add方法,以为只是测试嘛。,此外关于User类就不标注了(就是一个JavaBean);
4.测试如下
package com.dong.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.dong.dao.UserDao;
import com.dong.model.User;
public class TestCase {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("classpath:spring_jtom.xml");
UserDao useDao=(UserDao)ctx.getBean("userDao");
User u1=new User();
u1.setAge(20);
u1.setId(400895623);
u1.setAddress("beijing");
u1.setSex("male");
//name在数据库jtom_t1,user表中不能为Null,在jtom_t2中可以为null
u1.setName(null);
//入库
useDao.add(u1);
}
}
5执行结果如下:
Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into user (id,name,age,sex,address) values(?,?,?,?,?)]; Column 'name' cannot be null; nested exception is com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Column 'name' cannot be null
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:101)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:603)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:812)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:868)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:872)
at com.dong.dao.UserDao.add(UserDao.java:22)
at com.dong.dao.UserDao$$FastClassByCGLIB$$71e62aae.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at com.dong.dao.UserDao$$EnhancerByCGLIB$$f875683.add(<generated>)
at com.dong.test.TestCase.main(TestCase.java:23)
Caused by: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Column 'name' cannot be null
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1169)
at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:693)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1404)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1318)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1303)
at org.enhydra.jdbc.core.CorePreparedStatement.executeUpdate(CorePreparedStatement.java:102)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:818)
at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:1)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:587)
... 15 more
说明:因为name字段在数据库jtom_t1,user表中不能为null,导致数据插入失败,事务回滚,最后jtom_t1与jtom_t2的user表中的数据记录都为空。说明template1与template2在同一个事务中,而它俩面对是不同的数据库;
6.关于jar包的说明
(1).jtom需要的bao为:
(2).spring需要的包:
(3).其他jar包