Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 650|回复: 0

springJdbc(jdbcTemplate)事物拦截失效问题解决

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-9-4 14:59:51 | 显示全部楼层 |阅读模式

    先贴上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> 

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-22 12:52 , Processed in 0.061884 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表