论坛首页 Java企业应用论坛

Spring中事物管理一二

浏览 9408 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-09-07  

     由于对Java中的事物管理的概念(本地事物和jta事物)不是很清楚,Google好多文章都是很抽象,希望在论坛中大家提供点比较实际的程序代码案例和必须使用jta事物的场景;好啦,下面给个具体的代码(mybatis,spring),做下说明,我使用两个数据源,两个sqlSessionFactory,两个事物管理器,分别用于两种方式(mybatis的MappBean和MappXML)操作数据库。

 

spring配置如下:

	<bean id="dataSource1" class="com.jolbox.bonecp.BoneCPDataSource">
		<constructor-arg index="0" ref="dbConfig1"/>
	</bean>
	
	<bean id="dataSource2" class="com.jolbox.bonecp.BoneCPDataSource">
		<constructor-arg index="0" ref="dbConfig2"/>
	</bean>

<!-- 	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" /> -->

	<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" />
		<property name="dataSource" ref="dataSource1" />
	</bean>
	<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 		<property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" /> -->
		<property name="dataSource" ref="dataSource2" />
	</bean>

	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory1" />
	</bean>
	
	<bean id="userDAO" class="edu.silence.mybatis.dao.impl.userDAO">
		<property name="sqlSessionTemplate" ref="sqlSession"/>
	</bean>
	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource1" />
	</bean>
	
	<bean id="springTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource2" />
	</bean>
	
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="edu.silence.mybatis.dao.UserMapper" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory2" />
	</bean>

	<bean id="userService" class="edu.silence.mybatis.service.impl.UserService">
		<property name="userMapper" ref="userMapper"/>
		<property name="userDAO" ref="userDAO"/>
	</bean>
	
	<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
       <tx:attributes>
			<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="*" propagation="SUPPORTS"/>
		</tx:attributes>
   </tx:advice>
  
  <tx:advice id="txAdvice1" transaction-manager="transactionManager">
       <tx:attributes>
			<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="*" propagation="SUPPORTS"/>
		</tx:attributes>
   </tx:advice>
   
   <aop:config>
       <aop:pointcut id="interceptorPointCuts"
           expression="execution(* *..service.*.*(..))" />
       <aop:advisor advice-ref="txAdvice"  pointcut-ref="interceptorPointCuts" />
       <aop:advisor advice-ref="txAdvice1"  pointcut-ref="interceptorPointCuts" />
   </aop:config> 

 

dao代码如下:

public class userDAO implements IUserDAO {

	private SqlSessionTemplate sqlSessionTemplate;
	
	public SqlSessionTemplate getSqlSessionTemplate() {
		return sqlSessionTemplate;
	}

	public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
		this.sqlSessionTemplate = sqlSessionTemplate;
	}

	@Override
	public User findUser(String uid) {
		return (User) sqlSessionTemplate.selectOne("edu.silence.mybatis.dao.UserMapper.selectByPrimaryKey", uid);
	}

	@Override
	public int saveUser(User user) {
		boolean flag = true;
		if(flag){
			throw new RuntimeException("事物回滚......");
		}
		return sqlSessionTemplate.insert("edu.silence.mybatis.dao.UserMapper.insertSelective", user);
	}

}

 

业务层代码如下:

	private UserMapper userMapper;
	
	private IUserDAO userDAO;

	public UserMapper getUserMapper() {
		return userMapper;
	}

	public void setUserMapper(UserMapper userMapper) {
		this.userMapper = userMapper;
	}

	public IUserDAO getUserDAO() {
		return userDAO;
	}

	public void setUserDAO(IUserDAO userDAO) {
		this.userDAO = userDAO;
	}

	@Override
	public User findUser(String uid) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int saveUser(User user) {
		user.setUid(UUID.randomUUID().toString());
		int i = userMapper.insert(user);
		user.setAlias("就是我");
		user.setUid(UUID.randomUUID().toString());
		int j = userDAO.saveUser(user);
		if(i!=1 && j!=1){
			throw new RuntimeException("事物回滚......");
		}
		return i+j;
	}
	
 

测试代码如下:

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:**/*application*.xml");
		IUserService service = ctx.getBean("userService", IUserService.class);
		User user = new User();
		user.setAlias("Silence");
		user.setBirthday(new Date());
		user.setEmail("silence@email.com");
		user.setGender(1);
		user.setMobile("18674970916");
		user.setPassword("silence");
		user.setPhoto("head.jpg");
		user.setRealname("china");
		user.setRole(1);
		user.setStudystep(1);
		user.setUsername("silence");
		service.saveUser(user);
	}

 

上面使用本地事物,都能正常保存,如果失败都可以同时回滚,所以不明白jta到里在那个具体场景必须使用;也请各位讲解下上面代码在事物性方面有那些问题。

 

在下先O(∩_∩)O谢谢!!!

   发表时间:2011-09-08  
    其实就是jdbc事物和jta事物

  jdbc就是基于一个连接内的实物 可以作为一个原子操作

  jta是针对一个是原子操作内有多个数据源或者简单说有多个jdbc,然后把jdbc纳入jta容器管理,作为一个原子操作
0 请登录后投票
   发表时间:2011-09-08   最后修改:2011-09-08
a book for you --- <<Java Transaction>>
0 请登录后投票
   发表时间:2011-09-09   最后修改:2011-09-09

谢谢回复,算是明白啦!

其中上面的代码使用的是两个数据源,两个事务管理器;它们的工作结果大部分情况下能够保持和jta事务运行的结果,但不能完全保证,毕竟它们是俩个事务,只是两个事务定义了一个相同的事务边界。

把上面的代码使用jta实现(jotm2.2.1),唯一修改的是配置文件,和新增一个工厂bean(spring3中没有对jotm的官方支持),俺是从spring直接的版本拷贝过来的,代码如下:

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();
		}
	}

}

 xml修改如下:

	<!-- JOTM本地实例 -->
	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
	
	<!-- JTA事务管理器 -->
	<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
	    <!-- 指定userTransaction属性 -->
	    <property name="userTransaction" ref="jotm" />
	</bean>
	
	
	<!-- XAPool配置,内部包含了一个XA数据源,对应test1数据库 -->
	<bean id="topicDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
	    <property name="dataSource">
	        <!-- 内部XA数据源 -->
	        <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://10.1.0.117:3306/test?characterEncoding=utf8" />
	        </bean>
	    </property>
	    <property name="user" value="mysql" />
	    <property name="password" value="silence" />
	</bean>
	<!-- XAPool配置,内部包含了一个XA数据源,对应test2数据库 -->
	<bean id="postDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
	    <property name="dataSource">
	        <!-- 内部XA数据源 -->
	        <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://10.1.0.117:3306/test1?characterEncoding=utf8" />
	        </bean>
	    </property>
	    <property name="user" value="mysql" />
	    <property name="password" value="silence" />
	</bean>


	<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="mapperLocations" value="classpath*:edu/silence/mybatis/dao/**/*.xml" />
		<property name="dataSource" ref="topicDS" />
	</bean>
	<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="postDS" />
	</bean>

	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory1" />
	</bean>
	
	<bean id="userDAO" class="edu.silence.mybatis.dao.impl.userDAO">
		<property name="sqlSessionTemplate" ref="sqlSession"/>
	</bean>
	
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="edu.silence.mybatis.dao.UserMapper" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory2" />
	</bean>

	<bean id="userService" class="edu.silence.mybatis.service.impl.UserService">
		<property name="userMapper" ref="userMapper"/>
		<property name="userDAO" ref="userDAO"/>
	</bean>
	
	<tx:advice id="txAdvice" transaction-manager="txManager">
       <tx:attributes>
			<tx:method name="find*" propagation="REQUIRED" read-only="true"/>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="*" propagation="SUPPORTS"/>
		</tx:attributes>
   </tx:advice>
  
   <aop:config>
       <aop:pointcut id="interceptorPointCuts" expression="execution(* *..service.*.*(..))" />
       <aop:advisor advice-ref="txAdvice"  pointcut-ref="interceptorPointCuts" />
   </aop:config> 
 

 

0 请登录后投票
   发表时间:2011-09-09  
你这个ibatis配置有点老,我记得,有个什么scaner类,只要配置基础包,不需要写具体mapper的类名就可以自动找到所有mapper!!!!!!!!!
关于事务,你这里都是用切点配置的
我习惯于用注解配置,那样可以灵活的为每个方法配置详细事务属性!!!!!!!!!
0 请登录后投票
   发表时间:2011-09-10  

楼上所说的是    org.mybatis.spring.mapper.MapperScannerConfigurer类吧,谢谢提醒!个人也是第一次发帖,没注意那么多,写完可以运行就每管那么多啦

我上面的代码改写成如下:

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="edu.silence.mybatis.dao" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory2"/>
	</bean>

 spring提供四种事务配置方式

1、使用代理

2、使用拦截器

3、使用tx标签

4、使用注解

这些都是spring逐步发展添加进来的,个人平时用得最多的是后面两种,如果是一个新项目,或者项目命名比较统一时

我个人一般使用第三种,如果是遗留项目合作项目命名不是和统一,我使用第4种;第三种只要写一个配置就可以,

第4种要每个去写注解,比较浪费O(∩_∩)O~;两种各有各得好处,谢谢大家的回复,有什么经验分享的请大家跟帖,谢谢。。。

 

 

0 请登录后投票
   发表时间:2011-09-10  
lz是讨论分布式事务?

0 请登录后投票
   发表时间:2011-09-10  
blog 写道

楼上所说的是    org.mybatis.spring.mapper.MapperScannerConfigurer类吧,谢谢提醒!个人也是第一次发帖,没注意那么多,写完可以运行就每管那么多啦

我上面的代码改写成如下:

	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="edu.silence.mybatis.dao" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory2"/>
	</bean>

 spring提供四种事务配置方式

1、使用代理

2、使用拦截器

3、使用tx标签

4、使用注解

这些都是spring逐步发展添加进来的,个人平时用得最多的是后面两种,如果是一个新项目,或者项目命名比较统一时

我个人一般使用第三种,如果是遗留项目合作项目命名不是和统一,我使用第4种;第三种只要写一个配置就可以,

第4种要每个去写注解,比较浪费O(∩_∩)O~;两种各有各得好处,谢谢大家的回复,有什么经验分享的请大家跟帖,谢谢。。。

 

 

 

5. Spring 提供了原子性的事务操作, TransactionTemplate,和其他 Template 一样操作,可以控制到代码块事务。

 

0 请登录后投票
   发表时间:2011-09-10  
代码中如果同时使用两个数据源,或者还有其他,比如 JMS,理论必须使用JTA。

楼主根本不能保证事务一致性。

在一个方法(作为一个操作单元)中,有如下操作。

1.对 datasource 1 插入数据
2.对 datasource 2 插入数据

如果1 成功,2 失败,作为一个事务单元,它应该是失败的,所有事务应该回滚,但楼主方法无法控制这种情况发生。
0 请登录后投票
   发表时间:2011-09-17   最后修改:2011-09-17

      感谢hantsy 同学给俺补全,spring提供的TransactionTemplate,俺确实没用过;加上,就有五种,不过就不能叫"spring提供四种事务配置方式",俺也不知道叫什么

列出来如下:

 

1、使用代理

2、使用拦截器

3、使用tx标签

4、使用注解

5、TransactionTemplate(编程式事务)

 

hantsy 写道
代码中如果同时使用两个数据源,或者还有其他,比如 JMS,理论必须使用JTA。

楼主根本不能保证事务一致性。

在一个方法(作为一个操作单元)中,有如下操作。

1.对 datasource 1 插入数据
2.对 datasource 2 插入数据

如果1 成功,2 失败,作为一个事务单元,它应该是失败的,所有事务应该回滚,但楼主方法无法控制这种情况发生。

 

对于这个"作为一个事务单元,它应该是失败的,所有事务应该回滚",确实是这样,一个事务管理多个数据源,保证操作原子性;我上面的方法并不是真正用来解决多数据源的事务,只是个人为对事务的一种探索;两个事务管理在同一个事务边界中的两个数据源的操作,一对一管理,在平常情况下一定程度上保证一个原子操作的假象。我尝试这种方式是想对本地事务和jta事务的学习;这段时间在学习这方面的东东,在开发中,我经常是在对业务设计上规避多数据源上的事务处理,尽可能做到短事务,jta事务对性能影响是本地事务的好多倍。

 

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics