在使用ssm中遇到使用了Spring的@Transactional注解加注了方法或service后,mybatis还是自动提交并且无法回滚的问题。在排查了代码的配置后发现,问题出现的原因主要是由于不同bean的配置位置造成的。
解决方法主要时调整bean声明的位置,主要思路如下:
(1)、数据库相关的配置(datasource、service、dao、事物处理的管理类以及使用事务处理的方法)统一放到根ApplicationContext的配置中。
(2)、web mvc相关的配置统一放到servlet的配置中。
本例数据库部分使用xml进行配置,ApplicationContext和DispatcherServlet使用基于java的配置。大致部分的源码如下:
1、mybatis的xml配置
mybatis在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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="api.landsem.mybatis.service.impl" />
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<!-- dataSource configuration -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="acquireIncrement" value="3" />
<property name="maxIdleTime" value="60" />
<property name="checkoutTimeout" value="18000" />
<property name="idleConnectionTestPeriod" value="180" />
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:api/landsem/mybatis/mapping/*.xml" />
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="api.landsem.mybatis.IDao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- transaction support -->
<!-- PlatformTransactionMnager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
2、ApplicationContext配置
ApplicationContext的java配置类源码如下:
package api.landsem.base.configuration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.ResourceBundleMessageSource;
import api.landsem.device.v1.configuration.DeviceApiV1RootConfiguration;
import api.landsem.iot.v1.configuration.IotApiV1RootConfiguration;
@Configuration
@ComponentScan({"api.landsem.base.bean"})
@ImportResource("classpath:applicationContext-*.xml")
@Import({DeviceApiV1RootConfiguration.class,IotApiV1RootConfiguration.class})
public class RootConfiguration {
@Bean("messageSource")
public MessageSource getResourceBundleMessageSourc() {
ResourceBundleMessageSource resource = new ResourceBundleMessageSource();
resource.setBasename("i18n/i18n");
return resource;
}
}
3、DispatcherServlet配置
DispatcherServlet的java配置类源代码如下:
package api.landsem.base.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import api.landsem.base.interceptor.LoggerInterceptor;
import api.landsem.device.v1.configuration.DeviceApiV1WebMvcConfiguration;
import api.landsem.iot.v1.configuration.IotApiV1WebMvcConfiguration;
@Configuration
@EnableWebMvc
@Import({DeviceApiV1WebMvcConfiguration.class,IotApiV1WebMvcConfiguration.class})
public class DefaultWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter{
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/static/**","/css/**");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
this.addLogInterceptors(registry);
super.addInterceptors(registry);
}
/**
* @Title: addLogInterceptors
* @Description: Add log interceptor to record request information.
* @param registry
*/
private void addLogInterceptors(InterceptorRegistry registry) {
InterceptorRegistration reg = registry.addInterceptor(new LoggerInterceptor());
if(null != reg) {
reg.addPathPatterns("/base/**","/wechat/**");
}
}
}