`
roverll
  • 浏览: 14000 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Spring 3集成 mybatis3 声明式事务,事务不能回滚后的思考

    博客分类:
  • java
阅读更多
之前为了练练手自己用spring3 集成 mybaits3,采用spring的声明式事务,发现数据的插入没有问题,但是异常时不能回滚,一开始的代码如下(是按mybatis-spring-1.0.2-reference.pdf):

dao层代码 mybatis 版本的:
public void insertBlog(Blog blog) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
		blogMapper.insertBlog(blog);
	}
public void addTag(long blogId, Tag tag) {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
		Long tagId = blogMapper.findTagId(tag.getTagName());
		if (tagId != null )
			blogMapper.addTagForBlog(new Tuple<Long, Long>(blogId, tagId));
		else {
			blogMapper.insertTag(tag);
			blogMapper
					.addTagForBlog(new Tuple<Long, Long>(blogId, tag.getId()));
		}
	}

dao层 jdbc版本的实例,代码太长,不罗列了:
@Override
	public void insertBlog(Blog blog) throws Exception {
		Connection connection = dataSource.getConnection();
		String sql = "insert into blog (title," + "create_date, last_modify)"
				+ " values(?,now(),now())";
		PreparedStatement a = connection.prepareStatement(sql);
		a.setString(1, blog.getTitle());
		a.execute();
		String getBlogId = "select LAST_INSERT_ID() as id";
		ResultSet rsid = a.executeQuery(getBlogId);
		if(rsid.first())
			blog.setBlogId(rsid.getLong(1));
	}

bean配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:common/MybatisConfig.xml"></property>
	</bean>


	<bean id="blogService" class="service.impl.BlogService">
		<property name="blogDao" ref="blogDaoMybatis" />
	</bean>

	<bean id="blogDaoMybatis" class="dao.BlogDaoWithMybatis">
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
	</bean>

事务配置:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/test" />
		<property name="username" value="root" />
		<property name="password" value="****" />
	</bean>

	<!-- init the spring transactionManager -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*Tsc" propagation="REQUIRED"
				rollback-for="java.lang.Exception" />
			<tx:method name="*" propagation="SUPPORTS" />
		</tx:attributes>
	</tx:advice>

service方法:
public void insertBlog(Blog blog) throws Exception {
		blogDao.insertBlog(blog);
	}
public void addTagToBlog(long blogId, Tag tag) throws Exception {
		blogDao.addTag(blogId, tag);
		// throw new RuntimeException("hhh");
	}
public void addBlogTestTsc(Blog blog, Tag tag) throws Exception {
		insertBlog(blog);
		addTagToBlog(blog.getBlogId(), tag);
	}

blog能正常插入,误以为事务工作正常,同样的配置,自己尝试了在service方法addTagToBlog里面抛出异常,发现blog和标签都正常插入了……后来自己又用纯jdbc写了一个插入问题依旧(后面会提到),在网上一直搜寻答案都未果(这也是为什么在这里写出来的原因)
问题的关键不是事务配置错误
首先datasource属性的问题,dbcp默人行为是自动提交,所以插入完数据库就自动提交了,于是改配置为:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/test" />
		<property name="username" value="root" />
		<property name="password" value="****" />
		<property name="defaultAutoCommit" value="false"></property>
	</bean>

发现使用上面的代码后数据怎么都插不进去了,看过spring官方文档事务部分,里面的dbcp数据源配置没有这句:
<property name="defaultAutoCommit" value="false">
。mybatis集成spring的文档也就是强调datasource要和spring事务里面配置的datasource一致,这时候思维就混乱了。


昨天又看着自己的jdbc版本的代码,就想:如果是我用spring的aop来实现事务,我怎么能能在动态生成的代理对象中获取到被代理对象方法里面获取的那个connection对象再去commit和rollback呢?脑袋愚钝,一下子没有想明白。后来就在spring的DataSourceTransactionManager代码里面doCommit方法和doRollBack方法上各打了一个断点,发现不抛异常时执行了doCommit,抛异常时执行了doRollBack。这回可以肯定的是我的配置没有错,也证明我怀疑是对的,spring在代理类中获取的connection和我在dao层代码中获取的不是一个。看了下spring的org.springframework.jdbc.[版本].jar中的类,发现了DataSourceUtils这个类有一个static方法
public static Connection doGetConnection(DataSource dataSource) throws SQLException

顿时豁然开朗,改掉自己jdbc版本的dao层代码获取connection的代码为:
Connection connection = DataSourceUtils.getConnection(dataSource);

再测试,一切ok,搞定。


个人理解:使用DataSourceUtils的getConnection方法时把该获取到的这个链接在当前线程环境中注册到spring事务的context中去了,这样在生成的代理对象中就可以获取到这个connection然后commit或者rollback之(这其中还是有不是很清楚的地方:整么区分一个线程环境中多个地方获取connection,暂时没有敢去看spring的源代码)

如法炮制,在mybatis-spring-1.0.2.jar包中找到了SqlSessionUtils,换取所有获取mybatis SqlSession代码为:
SqlSession sqlSession = SqlSessionUtils.getSqlSession(sqlSessionFactory)

经过测试也正常。
分享到:
评论
2 楼 roverll 2012-04-19  
发现该博客还是有人在看,有必要说明下
文章最后写的使用
SqlSession sqlSession = SqlSessionUtils.getSqlSession(sqlSessionFactory)
这个单单的写测试代码事务是没有问题,我来我直接简单的servlet整合spring把系统跑起来,返现页面第二次做数据访问报错:链接已关闭,有些天在忙其他的,忘记报什么错了,是mybatis-spring-1.0.2.jar本来就有的bug,官网上也有,大家可以查阅。
推荐使用mapper直接注入,一下是配置:
<bean id="blogMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="mappers.BlogMapper" />
<!-- value 是类路径(也就是接口) -->
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
	</bean>

<bean id="blogDaoMybatis" class="dao.BlogDaoWithMybatis">
		<property name="blogMapper" ref="blogMapper" />
	</bean>

java代码就不用贴了

另外,偶尔发现,mybatis返回的list数据集是通过cglib处理过的,这点表示疑惑。
按理是用cglib或者jdk proxy 生成一个 blogMapper接口对象即可,数据没有必要用
cglib处理。发现过程:gson 这个 java 处理json的框架不认cglib处理过的对象,jackson可以
1 楼 roverll 2012-03-15  
另外,<property name="defaultAutoCommit" value="false">
这个配置去掉也行(defaultAutoCommit = true),如果异常,事务会回滚,
再次说明事务不能回滚的原因与此无关

相关推荐

    spring+mybatis的声明式事务

    本文将深入探讨"spring+mybatis的声明式事务"这一主题,帮助你理解并掌握这一核心技术。 1. **Spring事务管理** Spring提供两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理通过编写代码来控制...

    Spring 整合mybatis(注解&xml版声明式事务).pdf

    Spring与MyBatis的整合,一方面利用Spring管理MyBatis的SqlSessionFactory和SqlSession,另一方面Spring通过AOP提供声明式事务管理,进一步简化了数据库操作代码。在声明式事务管理中,我们可以在业务逻辑方法上添加...

    struts2.3+spring3.2+mybatis3.1整合注释行事务处理(含jar包)

    在这个项目中,我们看到的是一个基于Struts2.3、Spring3.2和MyBatis3.1的集成应用,其中使用了注释式的事务管理。 首先,让我们来详细了解一下这三个框架: 1. **Struts2**:这是一个基于MVC设计模式的Java Web...

    Spring3+mybatis+mysql整合详解(一)

    6. **事务管理**:Spring提供了声明式事务管理,只需在服务层的方法上添加@Transactional注解,Spring就会自动处理事务的开启、提交或回滚。 7. **测试与调试**:整合完成后,可以使用JUnit或其他测试框架对服务层...

    spring3 struts2 Mybatis3 组件注解 事务注解 实例

    此外,`@Transactional`注解用于声明式事务管理,使得开发者无需显式管理事务边界。 Struts2是基于MVC设计模式的Web应用框架,它继承了Struts1的优秀特性,并结合了WebWork的优势。Struts2允许开发者使用注解来配置...

    springmvc3+spring+mybatis3整合项目 注解实现

    声明式事务管理更简洁,只需在需要事务的方法上添加@Transactional注解,Spring会自动进行事务的开启、提交或回滚。 数据库文件的提供意味着项目包含了创建和初始化数据库的脚本,这有助于快速搭建环境并进行数据...

    spring+mybatis的编码式事务

    因此,在实际开发中,推荐使用基于注解的声明式事务,通过`@Transactional`注解简化事务管理,让代码更清晰、易于维护。然而,了解和掌握编码式事务有助于理解Spring事务管理的底层原理,对于解决特定场景的问题或...

    spring与mybatis整合实现事务配置

    4. **事务配置**:在Spring中,事务管理有两种方式:编程式事务管理和声明式事务管理。通常我们采用声明式事务管理,通过`&lt;tx:annotation-driven&gt;`标签启用基于注解的事务管理。事务的传播行为、隔离级别、超时时间...

    spring+mybatis+事务

    关于事务管理,Spring提供了一种声明式事务管理方式,允许开发者在不直接接触事务API的情况下,通过配置来控制事务的边界。在Spring中,我们可以使用@Transactional注解来标记需要进行事务管理的方法,一旦发生异常...

    spring4+mybatis3 集成实例(二)

    Spring提供声明式事务管理,只需在Service层的方法上添加@Transactional注解,即可实现事务的自动开始、提交或回滚。这样,当出现异常时,Spring会自动回滚所有相关的数据库操作,保证数据的一致性。 此外,我们还...

    《spring+mybatis 企业应用实战》源码、类库全资料

    Spring 框架是 Java 企业级应用的基石,它允许开发者以声明式的方式管理对象及其依赖关系,通过 IoC(Inversion of Control,控制反转)容器来实现。Spring 的 AOP 功能则提供了模块化的拦截器,用于实现日志记录、...

    mybatis-spring-1.3.1.jar下载

    2. **事务管理**:通过集成Spring的事务管理器,MyBatis-Spring可以实现声明式事务控制,使得事务的处理变得更加简单和灵活。 3. **Mapper代理**:它会为每个Mapper接口创建一个Spring的Bean,这个Bean是一个动态...

    springmvc+mybatis+声明式事务管理

    在IT行业中,SpringMVC、MyBatis以及声明式事务管理是Java Web开发中的关键组件。这个项目结合了这三个核心技术,构建了一个高效、稳定的后端系统。以下将详细阐述这些技术及其相互配合的工作原理。 首先,...

    整合struts2-spring3-MyBatis3

    5. **事务管理**:Spring提供了声明式事务管理,只需在Service层的方法上添加@Transactional注解,即可自动处理事务的开始、提交或回滚。 6. **测试和优化**:完成上述配置后,进行单元测试和集成测试,确保各个...

    spring+Mybatis模板

    声明式事务管理更简洁,只需在 Service 类上添加 @Transactional 注解,Spring 就会自动处理事务的开启、提交、回滚。 8. **测试**:编写单元测试或集成测试,验证 Spring MVC 和 MyBatis 的配置是否正确,确保...

    Spring+Mybatis+MySql+Maven 简单的事务管理案例.rar

    总结来说,这个案例将展示如何在Spring中配置Mybatis和MySQL,以便在处理数据库操作时使用Spring的声明式事务管理。Maven将用于构建和管理整个项目,包括依赖的解决和项目的打包。通过学习这个案例,开发者可以深入...

    官方mybatis整合spring例子

    5. **AOP代理**:Spring的AOP功能可以帮助我们实现事务的声明式管理。只需在需要进行事务控制的方法上添加@Transactional注解,Spring就会自动管理事务的开始、提交或回滚。 6. **MyBatis的注解使用**:除了XML文件...

    spring3+mybatis整合案例

    4. **Spring的AOP事务管理**:Spring提供了声明式事务管理,通过在Service层的方法上添加@Transactional注解,可以自动进行事务的开启、提交、回滚。这使得事务管理变得简单,无需在代码中显式处理。 5. **Spring ...

    maven-spring-mybatis

    事务回滚测试则验证了Spring的声明式事务管理,确保在出现错误时数据的一致性。 5. **事务管理**:Spring提供了两种事务管理方式,编程式和声明式。在描述中提到的事务回滚,通常指的是声明式事务管理,通过在方法...

    Spring-Mybatis整合

    在 Spring 中,可以使用声明式事务管理,通过 `&lt;tx:annotation-driven&gt;` 配置启用基于注解的事务管理。在 Service 方法上添加 `@Transactional` 注解,Spring 会自动进行事务的开启、提交或回滚。 5. **AOP 切面...

Global site tag (gtag.js) - Google Analytics