使用SSM框架进行项目开发时,经常因为配置的问题,可能会遇到一些稀奇古怪的问题,比如说:明明已经配置了事务管理,并且也在需要进行事务控制的方法上添加了@Transactional
注解,服务也基本能够正常启动和使用,但是当事务控制的方法中出错时,之前执行的修改数据库的操作,没有被回滚。
另外事务控制失效的另一个明显表现就是任何一个改变数据库数据的操作,都会直接被提交,即操作一经执行,就可以立马在数据库中得以体现。
事务控制未生效的配置。
springmvc配置文件指定扫包路径
<context:component-scan base-package="com.alex"> #错误配置方式
spirng-mybatis配置文件
# 没有引入扫包规则
<!-- 引入数据库配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- 数据库配置 -->
<bean id="dataSource" class="com.alex.common.tools.DBConnection"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 连接池最大数量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--引入mybatis-config.xml-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath*:com/alex/ai/nlpc/mapper/*/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--basePackage指定要扫描的包,在此包之下的映射器都会被搜索到。 可指定多个包,包与包之间用逗号或分号分隔 -->
<property name="basePackage" value="com.alex.ai.nlpc.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置基于注解的申明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
问题分析
其实这并不是我们事务配置的问题,而是我们定义<context:component-scan base-package="包路径">
扫包规则的问题,在spirngmvc配置文件中,我们扫描了所有的ben文件,但是我们是使用spring进行事务管理的。但是我们的spring-mybatis配置文件中并没有配置扫包规则。
配置文件不同,管理域也是不一样的,在springmvc配置了扫描所有bean文件,虽然其中包含了springmvc管理的,也包含了spring管理的,但是我们的spring-mybatis文件中并没有扫描和管理spring的配置,所以,spring-mybatis配置文件并不能访问到springmvc中的spring管理。因此,即使配置了事务注解,也不会被spring管理,从而造成了出错了也不会对事务进行回滚。
问题处理
修改方式,让springmvc配置文件只扫描controller目录,并在spring-mybatis文件中,添加扫描和管理spring的配置。
修改后:
springmvc配置
<context:component-scan base-package="com.alex.controller">
spring-mybatis配置文件中添加
<context:component-scan base-package="com.alex.service">
因为我们通常都是在service层进行数据库的操作和控制事务,如果不是在service层进行处理,只需要将package路径修改到需要使用事务的包的路径即可。
亲测,重启运行后,事务控制的方法中出错后,事务正常回滚!
如果一个方法添加了事务控制,那么这个方法中的任何SQL操作,都不会立马对数据库的数据进行修改,而是当这个方法执行结束之后,这些对数据库修改的操作才会被提交。因此我们判断事务是否生效,并不需要执行到有错误出现,只需要观察事务控制方法内的一个SQL执行之后,数据库数据是否立马发生了改变,改变了即说明事务控制没有生效,反之即表示事务控制生效了!