先贴上web.xml和spring-jdbc.xml代码:
web.xml代码:
1 <context-param>
2 <param-name>contextConfigLocation</param-name>
3 <param-value>
4 classpath:config/spring-jdbc.xml,
5 classpath:config/spring-redis.xml
6 </param-value>
7 </context-param>
8
9 <listener>
10 <description>spring监听器</description>
11 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
12 </listener>
13
14 <servlet>
15 <servlet-name>SpringMvc</servlet-name>
16 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
17 <init-param>
18 <param-name>contextConfigLocation</param-name>
19 <param-value>
20 classpath:config/spring-mvc.xml
21 </param-value>
22 </init-param>
23 <load-on-startup>1</load-on-startup>
24 </servlet>
25 <servlet-mapping>
26 <servlet-name>SpringMvc</servlet-name>
27 <url-pattern>/</url-pattern>
28 </servlet-mapping>
29 <servlet>
spring-jdbc.xml代码:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans
3 xmlns="http://www.springframework.org/schema/beans"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:tx="http://www.springframework.org/schema/tx"
6 xmlns:aop="http://www.springframework.org/schema/aop"
7 xmlns:context="http://www.springframework.org/schema/context"
8 xsi:schemaLocation="
9 http://www.springframework.org/schema/beans
10 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
11 http://www.springframework.org/schema/tx
12 http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
13 http://www.springframework.org/schema/aop
14 http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
15 http://www.springframework.org/schema/context
16 http://www.springframework.org/schema/context/spring-context-4.2.xsd" default-autowire="byName" >
17
18 <!-- 引入属性文件 -->
19 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
20 <property name="locations">
21 <list>
22 <value>classpath:config/jdbc.properties</value>
23 <value>classpath:config/redis.properties</value>
24 <value>classpath:config/contract-mail.properties</value>
25 </list>
26 </property>
27 </bean>
28
29 <!-- 加载config.properties,为了使该注解生效:@Value("#{config['email.alarm.sender.email']}") -->
30 <bean id="config" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
31 <!-- false表示当没找到这个配置文件时,应用程序应该报错 -->
32 <property name="ignoreResourceNotFound" value="false" />
33 <property name="locations">
34 <list>
35 <value>classpath:config/config.properties</value>
36 </list>
37 </property>
38 </bean>
39
40 <!-- 配置数据源 -->
41 <bean name="dataSourceSpied" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
42 <property name="url" value="${jdbc_url}" />
43 <property name="username" value="${jdbc_username}" />
44 <property name="password" value="${jdbc_password}" />
45 <property name="initialSize" value="${jdbc_initialSize}" />
46 <property name="maxActive" value="${jdbc_maxActive}" />
47 <property name="minIdle" value="${jdbc_minIdle}" />
48 <property name="maxWait" value="${jdbc_maxWait}" />
49 <property name="validationQuery" value="${validationQuery}" />
50 <property name="testOnBorrow" value="false" />
51 <property name="testOnReturn" value="false" />
52 <property name="testWhileIdle" value="true" />
53 <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
54 <property name="timeBetweenEvictionRunsMillis" value="60000" />
55 <!-- 打开removeAbandoned功能 -->
56 <property name="removeAbandoned" value="true" />
57 <!-- 1800秒,也就是30分钟 -->
58 <property name="removeAbandonedTimeout" value="180" />
59 <!-- 关闭abanded连接时输出错误日志 -->
60 <property name="logAbandoned" value="true" />
61 <!-- 监控数据库 -->
62 <property name="filters" value="mergeStat" />
63 </bean>
64
65 <bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
66 <constructor-arg ref="dataSourceSpied"/>
67 </bean>
68
69 <!-- myBatis文件 -->
70 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
71 <property name="dataSource" ref="dataSource" />
72 <property name="mapperLocations" value="classpath*:mapper/*.xml" />
73 <property name="plugins">
74 <list>
75 <!-- 物理分页 -->
76 <bean class="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">
77 <property name="dialectClass" value="com.github.miemiedev.mybatis.paginator.dialect.OracleDialect"></property>
78 </bean>
79 </list>
80 </property>
81 </bean>
82
83 <!-- 配置事务管理器 -->
84 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
85 <property name="dataSource" ref="dataSourceSpied" />
86 </bean>
87 <!-- 声明式事务管理,拦截多个包下面事物 and execution(* com.xxx.service.*Service.*(..)) -->
88 <aop:config>
89 <aop:advisor pointcut="execution(* com.xxx.service.*Service.*(..))"
90 advice-ref="myAdvice"/>
91 </aop:config>
92 <tx:advice id="myAdvice" transaction-manager="transactionManager">
93 <tx:attributes>
94 <tx:method name="addCommandExec" propagation="REQUIRES_NEW"/> <!-- 这个名字的service方法使用新事物,不使用继承事物 -->
95 <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.RuntimeException" />
96 <!-- <tx:method name="*" read-only="true"/> -->
97 <!-- <tx:method name="*" read-only="true" rollback-for="com.smvc.util.DaoException"/> -->
98 </tx:attributes>
99 </tx:advice>
100
101 <!-- 自动扫描组件,多个包用逗号隔开,需要把controller去掉,否则影响事务管理 -->
102 <context:component-scan base-package="com.xxx,com.xxx.command">
104 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
105 </context:component-scan>
106
107 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
108 <property name = "dataSource" ref="dataSource" />
109 </bean>
110
111 <bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
112 <constructor-arg ref="dataSource" />
113 </bean>
114
115 </beans>
bug问题表现:
在Service方法中,事物不起作用,bug表现如下: 1) 期望:Service方法中save()方法执行前开启事物,执行后提交事物,提交事物后才可以在数据库里看到那条新insert的数据。
2) 现象:事物不起作用,在save()方法未结束时,数据库中已经可以看到那条新insert的数据。save()里使用的是jdbcTemplate对象执行的SQL。
问题解决思路:
由于是springMVC项目,为了更快的debug,省去tomcat启动时间,建立了Junit测试类模拟spring容器启动,代码贴上如下:
1 package com.xxx.service;
2 import org.apache.log4j.Logger;
3 import org.junit.Test;
4 import org.junit.runner.RunWith;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.test.context.ContextConfiguration;
7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
8 import org.springframework.test.context.web.WebAppConfiguration;
9
11 @WebAppConfiguration
12 @RunWith(SpringJUnit4ClassRunner.class)
13 @ContextConfiguration({ "classpath:config/spring-mybatis.xml"
14 ,"classpath:config/spring-redis.xml","classpath:config/spring-mvc.xml"})
15 public class SystemUserServiceTest {
16 private final static Logger logger= Logger.getLogger(SystemUserServiceTest.class);
17
18 @Autowired
19 private SystemUserService systemUserService;
20 @Test
21 public void test() {
22 int result2 = systemUserService.insertSystemUser();
23 logger.info("result2 == " + result2);
24 }
25
26 }
第一次解决方法:百度了很多资料,都说spring-mvc.xml和spring-jdbc.xml的注解扫描器要注意互相排除,也就是controller层的要排除扫描service类,service层的要排除controller层,我仔细对比了两个xml配置文件,与网上写的解决方法一致。所以不是这个原因出现的问题。
第二次解决方法:我的spring-jdbc.xml配置文件里面,使用的是transactionManager对象来做事物管理类,所以跟踪spring事物类org.springframework.jdbc.datasource.DataSourceTransactionManager,看看到底有没有进入事物管理。分别在DataSourceTransactionManager类里的doBegin事物开启和doCommit事物提交方法里打上断点,debug开始跟踪。debug过程中发现,spring容器确实在save()方法之前执行的doBegin()事物开启,确实在save()执行完毕之后执行的doCommit()事物提交。可是当save()方法还未彻底结束之前,也就是doCommit()方法也尚未执行之前,数据库就有这条新insert记录了!所以不是事物配置没有起作用的原因。
第三次解决方法:这次换个思路,我在save()方法中执行sql语句的代码是:jdbcTemplate.update(insertSql); 我在这个update方法里打了断点,一直跟踪下去,终于发现了bug原因!
bug原因:
jdbcTemplate.update(insertSql) 中的update方法里的dataSource对象在debug过程中看到此对象ID是111,而在之前DataSourceTransactionManager类里面的dataSource对象在debug过程中此对象ID是81,说明jdbcTemplate对象拿到的dataSource对象和事物管理器里面的dataSource对象根本就不是同一个!
bug修正:
把spring-jdbc.xml里的实际sql 执行对象所引用的dataSource换成和事物管理器的dataSource同一个对象就好了。
1 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
2 <property name = "dataSource" ref="dataSourceSpied" />
3 </bean>
4
5 <bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
6 <constructor-arg ref="dataSourceSpied" />
7 </bean>
|