Spring3.2+mybatis3.0+atomikos的jta系统搭建

我们有一个后台的项目,是用了好几个数据库链接,以前为了方便就用了多数据源的那种配置方法(不要吐槽呢),但是现在用户多了,经常会遇到不同数据库插入失败的问题。在我们当初使用这种架构的时候,就知道如果用这个框架会存在跨数据库事务无法处理,项目急呢。

现在有时间了,就准备处理一下这个遗留的bug,因为我们现在的框架用的是spring3.2+mybatis3.0,刚开始准备用jta中jotm来实现,结果在junitTest一直报org.springframework.transaction.jta.JotmFactoryBean  这个类找不到,后来查了一下spring的官方文档,原来Spring 3以上版本,去掉了JotmFactoryBean类,不能通过集成Jotm实现Jta功能,哎,只能怪我急攻心切,当时多查查文档也就不至于这样了,有多学了点知识呢,嘿嘿。现在只好用另一种形式了:atomikos,幸亏基本上都差不多呢。

其实在写这个系统的时候,我也在网上找了不少资料,但是大部分的都是只用了spring2,或者mybatis3,于是我就想写一个基于spring3+mybatis3的系统,方便大家互相学习呢,我第一次写博客,语言逻辑有问题的还请大家多多见谅呢。

1.数据库

在这里我用的是mysql的,新建了两个数据库分别为da1,da2

create database da1;
use da1;
CREATE TABLE `table1` (
  `id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
create database da2;
use da2;
CREATE TABLE `table2` (
  `id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.系统


2.1项目包截图

2.2基本类

在这里我只展示了其中的一个model实体类Table1.java,Table2和这个差不多

/**   
 * @文件名称: Tabel1.java
 * @类路径: com.kasiait.jta.da1.model
 * @描述: TODO
 * @作者:kasiait521
 * @时间:2014-04-24
 */
package com.kasiait.jta.da1.model;

import com.kasiait.jta.common.model.CommonPage;

public class Tabel1 {
	private Integer id;//自增长id
	private CommonPage page;//这个是我们系统中用来分页的,大家可以忽略呢

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public CommonPage getPage() {
		return page;
	}

	public void setPage(CommonPage page) {
		this.page = page;
	}

}

在这里我只展示了其中的一个mapper类Tabel1Mapper.java,Table2Mapper和这个差不多

/**   
 * @文件名称: Tabel1Mapper.java
 * @类路径: com.kasiait.jta.da1.mapper
 * @描述: TODO
 * @作者:kasiait521
 * @时间:2014-04-24
 */
package com.kasiait.jta.da1.mapper;

import java.util.List;
import com.kasiait.jta.da1.model.Tabel1;

public interface Tabel1Mapper {
	public void insertTabel1(Tabel1 t);
	public List<Tabel1> listPageAll(Tabel1 t);
}

service类Tabel1Service.java

/**   
 * @文件名称: Tabel1Service.java
 * @类路径: com.kasiait.jta.da1.service
 * @描述: TODO
 * @作者:kasiait521
 * @时间:2014-04-24
 */
package com.kasiait.jta.da1.service;

import java.util.List;
import com.kasiait.jta.da1.model.Tabel1;

public interface Tabel1Service {
	public void insertTabel1(Tabel1 t);
	public List<Tabel1> listPageAll(Tabel1 t);
}
serviceImpl类

/**   
 * @文件名称: Tabel1ServiceImpl.java
 * @类路径: com.kasiait.jta.da1.service.impl
 * @描述: TODO
 * @作者:kasiait521
 * @时间:2014-04-24
 */
package com.kasiait.jta.da1.service.impl;

import java.util.List;
import com.kasiait.jta.da1.mapper.Tabel1Mapper;
import com.kasiait.jta.da1.model.Tabel1;
import com.kasiait.jta.da1.service.Tabel1Service;

public class Tabel1ServiceImpl implements Tabel1Service {

	private Tabel1Mapper tabel1Mapper;//spring注入,由于涉及到底层数据源关系,所以我就在配置文件中写的,还希望注解狂

	public Tabel1Mapper getTabel1Mapper() {
		return tabel1Mapper;
	}

	public void setTabel1Mapper(Tabel1Mapper tabel1Mapper) {
		this.tabel1Mapper = tabel1Mapper;
	}

	@Override
	public void insertTabel1(Tabel1 t) {
		// TODO Auto-generated method stub
		tabel1Mapper.insertTabel1(t);
	}

	@Override
	public List<Tabel1> listPageAll(Tabel1 t) {
		// TODO Auto-generated method stub
		return tabel1Mapper.listPageAll(t);
	}
} 
/**   
 * @文件名称: Tabel2ServiceImpl.java
 * @类路径: com.kasiait.jta.da2.service.impl
 * @描述: TODO
 * @作者:kasiait521
 * @时间:2014-04-24
 */
package com.kasiait.jta.da2.service.impl;

import com.kasiait.jta.da1.mapper.Tabel1Mapper;
import com.kasiait.jta.da1.model.Tabel1;
import com.kasiait.jta.da2.mapper.Tabel2Mapper;
import com.kasiait.jta.da2.model.Tabel2;
import com.kasiait.jta.da2.service.Tabel2Service;

public class Tabel2ServiceImpl implements Tabel2Service {
    private Tabel2Mapper tabel2Mapper;// 数据源2

    private Tabel1Mapper tabel1Mapper;// 数据源1同时注入到这个impl类

    public Tabel1Mapper getTabel1Mapper() {
        return tabel1Mapper;
    }

    public void setTabel1Mapper(Tabel1Mapper tabel1Mapper) {
        this.tabel1Mapper = tabel1Mapper;
    }

    public Tabel2Mapper getTabel21Mapper() {
        return tabel2Mapper;
    }

    public void setTabel2Mapper(Tabel2Mapper tabel2Mapper) {
        this.tabel2Mapper = tabel2Mapper;
    }

    @Override
    public void insertTabel2(Tabel2 t) {// 在这里进行处理的都会在一个事物里
        // TODO Auto-generated method stub
        Tabel2 t2 = new Tabel2();
        t2.setId(10);
        Tabel1 t1 = new Tabel1();
        t1.setId(10);
        tabel2Mapper.insertTabel2(t2);// 向数据库da2里插入数据
        tabel1Mapper.insertTabel1(t1);// 向数据库da1里插入数据
    }

}
controller类

@Controller
@RequestMapping("/kaisiat")
public class FrontIndexController {

	@Autowired
	private Tabel1Service tabel1Service;

	@Autowired
	private Tabel2Service tabel2Service;

	/**
	 * 
	 * @描述: 网站入口
	 * @作者: kasiait521
	 * @日期:2014-04-24
	 * @修改内容
	 * @参数: @return
	 * @return String
	 * @throws
	 */
	@RequestMapping(value = "index", method = RequestMethod.GET)
	public ModelAndView index(HttpServletRequest request,
			HttpServletResponse response, HttpSession session) {
		// 在这里我只是模拟了操作,但并未转到具体的页面中,只是一个测试而已;
		// 如果系统配置好了,第一次执行时没有问题的,第二次的时候,你可以把tabel2Service中的实现类的da2改一下插入的数据;
		// 因为数据库中默认的都是主键所以都是唯一值,这样如果没有事物的话,da1是插入失败,da2是插入成功的;
		// 所以正好测试一下,结果肯定是da1和da2都插入失败了,因为事物回滚了。
		ModelAndView mv = new ModelAndView();
		Tabel2 t2 = new Tabel2();
		tabel2Service.insertTabel2(t2);
		return mv;
	}

}


3.配置文件

mybatis/da1中的sqlmap-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN"  
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias type="com.kasiait.jta.da1.model.Tabel1"
			alias="Tabel1" />
	</typeAliases>
        <--系统分页,可忽略-->
        <plugins>
		<plugin interceptor="com.kasiait.framework.plugin.PagePlugin">
			<property name="dialect" value="mysql" />
			<property name="pageSqlId" value=".*listPage.*" />
		</plugin>
	</plugins>
	<mappers>
		<mapper resource="mybatis/da1/Tabel1.xml" />
	</mappers>
</configuration>

mybatis/da1中的Tabel1.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kasiait.jta.da1.mapper.Tabel1Mapper">
	<!-- 从数据库到java实体对象映射声明 -->
	<resultMap type="Tabel1" id="Tabel1ResultMap">
		<result column="id" property="id" />
	</resultMap>
	
	<insert id="insertTabel1" parameterType="Tabel1">
		insert into table1 (id)	values (#{id})
	</insert>
	
	<select id="listPageAll" resultMap="Tabel1ResultMap">
		select * from table1
	</select>
</mapper>

ApplicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
						http://www.springframework.org/schema/aop 
						http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
						http://www.springframework.org/schema/tx 
						http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
						http://www.springframework.org/schema/jee 
						http://www.springframework.org/schema/jee/spring-jee-3.0.xsd 
						http://www.springframework.org/schema/context 
						http://www.springframework.org/schema/context/spring-context-3.0.xsd"
	default-lazy-init="true">

	<import resource="ApplicationContext-service.xml" />
	<!-- spring atomikos -->
	<!-- mysql da1数据源 这里没有从配置文件中获取,大家可以修改一下呢 -->
	<bean id="mysqlDA1" class="com.atomikos.jdbc.AtomikosDataSourceBean"
		init-method="init" destroy-method="close">
		<description>mysql first datasource</description>
		<property name="uniqueResourceName">
			<value>mysql_da1</value>
		</property>
		<property name="xaDataSourceClassName">
			<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
		</property>
		<property name="xaProperties">
			<props>
				<prop key="user">root</prop>
				<prop key="password">root</prop>
				<prop key="url">jdbc:mysql://127.0.0.1:3306/da1
				</prop>
			</props>
		</property>
		<property name="poolSize" value="10" />
	</bean>

	<!-- mysql da2数据源 -->
	<bean id="mysqlDA2" class="com.atomikos.jdbc.AtomikosDataSourceBean"
		init-method="init" destroy-method="close">
		<description>mysql second datasource</description>
		<property name="uniqueResourceName">
			<value>mysql_da2</value>
		</property>
		<property name="xaDataSourceClassName">
			<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
		</property>
		<property name="xaProperties">
			<props>
				<prop key="user">root</prop>
				<prop key="password">root</prop>
				<prop key="url">jdbc:mysql://127.0.0.1:3306/da2
				</prop>
			</props>
		</property>
		<property name="poolSize" value="10" />
	</bean>

	<!-- atomikos事务经管器 -->
	<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
		init-method="init" destroy-method="close">
		<description>UserTransactionManager</description>
		<property name="forceShutdown">
			<value>true</value>
		</property>
	</bean>

	<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
		<property name="transactionTimeout" value="300" />
	</bean>

	<!-- spring 事务经管器 -->
	<bean id="springTransactionManager"
		class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="transactionManager">
			<ref bean="atomikosTransactionManager" />
		</property>
		<property name="userTransaction">
			<ref bean="atomikosUserTransaction" />
		</property>
	</bean>

	<!-- 通知配置 -->
	<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
		<tx:attributes>
			<tx:method name="delete*" rollback-for="Exception" />
			<tx:method name="save*" rollback-for="Exception" />
			<tx:method name="update*" rollback-for="Exception" />
			<tx:method name="*" read-only="true" rollback-for="Exception" />
		</tx:attributes>
	</tx:advice>

	<!-- 事务切面配置 -->
	<aop:config>
		<aop:pointcut id="serviceOperation" expression="execution(* *..service*..*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
	</aop:config>

	<!-- 第一个数据源 -->
	<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="mysqlDA1" />
		<property name="configLocation" value="classpath:mybatis/da1/sqlmap-config.xml" />
	</bean>
	
	<!-- 第二个数据源 -->
	<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="mysqlDA2" />
		<property name="configLocation" value="classpath:mybatis/da2/sqlmap-config.xml" />
	</bean>

</beans>
ApplicationContext-mvc.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" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd	
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!--mvc:annotation-driven/ -->
	<context:component-scan
		base-package="com.kasiait.jta, com.kasiait.jta.da1, com.kasiait.jta.da2" />

	<!-- 使用方法级拦截器 -->
	<bean id="methodInvokerIntercepterManager"
		class="org.springframework.web.servlet.mvc.annotation.MethodInvokerIntercepterManager">
		<property name="intercepters">
			<list>
				<bean class="com.kasiait.framework.interceptor.PrivilegeIntercepter" />
			</list>
		</property>
	</bean>

	<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
		<property name="methodInvokerIntercepterManager" ref="methodInvokerIntercepterManager" />
	</bean>

	<!-- springmvc视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<!-- 处理JSON数据转换的 -->
	<bean id="mappingJacksonHttpMessageConverter"
		class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
		<!-- 为了处理返回的JSON数据的编码,默认是ISO-88859-1的,这里把它设置为UTF-8,解决有乱码的情况 -->
		<property name="supportedMediaTypes">
			<list>
				<value>text/html;charset=UTF-8</value>
			</list>
		</property>
	</bean>
</beans>

ApplicationContext-service.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.xsd">

	<!-- 因为没有用注解,所以只好在这里配置了,我觉得这样以后维护起来比较方便呢 -->
	<bean id="tabel1Mapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="sqlSessionFactory" ref="sqlSessionFactory1" />
		<property name="mapperInterface" value="com.kasiait.jta.da1.mapper.Tabel1Mapper" />
	</bean>

	<bean id="tabel2Mapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="sqlSessionFactory" ref="sqlSessionFactory2" />
		<property name="mapperInterface" value="com.kasiait.jta.da2.mapper.Tabel2Mapper" />
	</bean>
	<bean id="tabel1Service" class="com.kasiait.jta.da1.service.impl.Tabel1ServiceImpl">
		<property name="tabel1Mapper" ref="tabel1Mapper" />
	</bean>

	<bean id="tabel2Service" class="com.kasiait.jta.da2.service.impl.Tabel2ServiceImpl">
		<property name="tabel2Mapper" ref="tabel2Mapper" />
		<property name="tabel1Mapper" ref="tabel1Mapper" />
	</bean>
</beans>

jta.properties jta的配置文件

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name=tm.out
com.atomikos.icatch.log_base_name=tmlog
com.atomikos.icatch.tm_unique_name=tm
com.atomikos.icatch.console_log_level=INFO

到这里就基本结束了,刚开始写博客,没想到写了好几个小时,不过还是蛮幸福的,大家一起分享,一起进步!

下面是这个项目中atomikos的jar包,其他的spring3.2+mybatis3的包,网上有很多呢,我就不上传了

http://download.csdn.net/detail/kasiait521/7249723





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring+iBatis+JOTM实现JTA事务: 如何处理跨库事物:spring + jtom 的jta事务是个很好的选择. 这个源码示例非常不错,包括所有的源码和jar包,下载后eclipse 或 myeclipse 导入就能用。 里面有详细的说明和注释,欢迎下载传播。有问题请在评价中留言,我会及时回复的。 <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/> <!-- JTA事务管理器 --> <bean id="myJtaManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction"> <ref local="jotm"/> </property> </bean> <!-- 数据源A --> <bean id="dataSourceA" 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="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> </bean> </property> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 数据源B --> <bean id="dataSourceB" 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="${jdbc2.driver}"/> <property name="url" value="${jdbc2.url}"/> </bean> </property> <property name="user" value="${jdbc2.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 事务切面配置 --> <aop:config> <aop:pointcut id="serviceOperation" expression="execution(* *..servi1ce*..*(..))"/> <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> </aop:config> <!-- 通知配置 --> <tx:advice id="txAdvice" transaction-manager="myJtaManager"> <tx:attributes> <tx:method name="delete*" rollback-for="Exception"/> <tx:method name="save*" rollback-for="Exception"/> <tx:method name="update*" rollback-for="Exception"/> <tx:method name="*" read-only="true" rollback-for="Exception"/> </tx:attributes> </tx:advice ...... ...... ......

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值