Spring事务管理配置文件问题排查

在开发中,遇到了sql语句报错,但是并没有回滚的情况。

经过几天的排查,终于找到了事务没有回滚的原因。

原来的项目用的是informix的数据库,原来针对事务回滚的机制都是好用的。我本地用的是mysql数据库。

先将程序代码与spring-mybatis.xml配置文件拿过来:

1、程序代码:

这个问题是在验证增删改查返回值时发现的。

两个操作,删除时,因为关联了外键,所以会报错,此时正常情况更新的语句也会回滚,但是并没有。

/**
   *@Author: Administrator on 2020/3/12 15:15
   *@param:
   *@return:
   *@Description:查询同步情况
   */
  @Override
  public PageInfo getSyncstatusPages(Syncstatus vo, int pageNo, int pageSize) {
    PageHelper.startPage(pageNo, pageSize);

    /* //查看增删改查的返回值
    //1新增:返回值自己定义,可以是void,int
    //1-1新增一条数据:插入成功,返回值为1
    int insert_success1 = yylfHttpServletMapper.insert("8", "2", "1");
    //1-2新增多条数据:插入成功,返回值为插入的数据条数,当有一条数据错误时,所有数据都会插入失败
    int insert_success2 = yylfHttpServletMapper.insert_duotiao("7");
    String insert_success3 = yylfHttpServletMapper.insert_duotiao_String("7");//不支持返回值为String类型
    //1-3新增一条数据:插入失败:主键冲突,会直接报异常
    int insert_failed = yylfHttpServletMapper.insert("1", "2", "1");
    //1-4插入null:属性为null,如果表中所有字段允许为null,插入一条所有值均为null的数据
    Syncstatus syncstatus1 = null;
    yylfHttpServletMapper.insertSyncstatus(syncstatus1);
    //1-5插入一个没有赋值的对象:属性为null,如果表中所有字段允许为null,插入一条所有值均为null的数据
    Syncstatus syncstatus2 = new Syncstatus();
    yylfHttpServletMapper.insertSyncstatus(syncstatus2);*/

    /*//2删除:返回值自己定义,可以是void,int
    //2-1删除成功:没有数据:返回值为0
    int delete_success1 = yylfHttpServletMapper.delete("0");
    //2-2删除成功:有多条数据:返回值为删除的数据条数
    int delete_success2 = yylfHttpServletMapper.delete_systemcode("2");*/
    //2-3删除失败:例如有外键:报异常

    //3更新:返回值自己定义,可以是void,int
    //3-1更新成功:没有数据,返回值为0
    //int update_no = yylfHttpServletMapper.update_no("0");
    //3-2更新成功:有多条数据,返回更新的数据条数
    int update_duotiao = yylfHttpServletMapper.update_duotiao_systemcode("2");
    int delete_fail = yylfHttpServletMapper.delete("1");
    //3-3更新失败:例如有外键,报异常
    //int update_fail = yylfHttpServletMapper.update_fail("1");

    //4查询
    //4-1 没数:String 类型返回null
    //Object object = yylfHttpServletMapper.select("0");
    //4-1 没数:集合 类型返回[]空集合
    //Syncstatus syncstatus3 = new Syncstatus();
    //syncstatus3.setStatus("7");
    //List<Syncstatus> page0 = yylfHttpServletMapper.getSyncstatusList(syncstatus3);
    //4-1 没数:int 类型返回null,如果定义为int会报错。因为没数时返回null,可以将返回类型改为String
    //String i = yylfHttpServletMapper.select_int(0);
    //4-1:当返回值为对象时,若返回值为空,则返回null
    //4-2 有数
    List<Syncstatus> pages = yylfHttpServletMapper.getSyncstatusList(vo);
    return new PageInfo<Syncstatus>(pages);
  }

2、对数据库的操作:

<update id="update_duotiao_systemcode">
  UPDATE aaa SET systemcode = '3' WHERE systemcode = #{systemcode,jdbcType=VARCHAR}
 </update>

<delete id="delete">
  delete from aaa where uuid = #{uuid,jdbcType=VARCHAR}
 </delete>

3、配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
    <constructor-arg ref="dataSourceTarget"/>
  </bean>

  <!-- 定义使用dbcp2连接池的数据源
      此处使用自定义的数据源,将用户名与密码解密处理
   -->
  <bean id="dataSourceTarget" class="com.asd.common.jdbc.MyBasicDataSource" >
    <property name="url" value="${jdbc.url}"> </property>
    <property name="username" value="${jdbc.username}"> </property>
    <property name="password" value="${jdbc.password}"> </property>
    <property name="driverClassName" value="${jdbc.driverClassName}"> </property>
    <!-- informix-->
    <!--<property name="validationQuery" value="select count(*) from systables"> </property>-->
    <!-- mysql检测方式 -->
     <property name="validationQuery" value="select 1"> </property>
    <!-- oracle检测方式
    <property name="validationQuery" value="select 1 from dual"> </property> -->
  </bean>

  <!-- 配置SqlSessionFactoryBean -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 注入数据源 相关信息看源码 -->
    <property name="dataSource" ref="dataSource" />
    <!-- 扫描的实体所在的包-->
    <property name="configLocation" value="classpath:mybatis.xml"/>
    <!-- mapper和resultmap配置路径 -->
    <property name="mapperLocations">
      <list>
        <value>classpath:mybatis/*Mapper.xml</value>
      </list>
    </property>
  </bean>

  <!-- 自动扫描mapper接口,注入sqlsessionfactory -->
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.asd.modules.dao"/>
  </bean>

  <!-- 启用类扫描机制,通过元数据配置Service -->
  <context:component-scan base-package="com.asd">
    <context:include-filter type="regex"
                expression="com\.asd\.modules\.sevice\.impl\.*ServiceImpl" />
  </context:component-scan>

  <!-- mybatis事物配置 -->

  <context:annotation-config />
  <!-- ================================事务相关控制================================================= -->

  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>

  <tx:advice id="reinsAdvice" transaction-manager="transactionManager">
    <tx:attributes>

      <tx:method name="delete*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />

      <tx:method name="insert*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
      <tx:method name="save*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />

      <tx:method name="update*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.Exception" />

      <tx:method name="find*" propagation="SUPPORTS" />
      <tx:method name="get*" propagation="SUPPORTS" />
      <tx:method name="select*" propagation="SUPPORTS" />
      <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <!-- 把事务控制在Service层 -->
    <aop:pointcut id="reinsPointCut"
           expression="execution(* com.asd.modules.service.impl.*ServiceImpl.*(..))" />
    <aop:advisor pointcut-ref="reinsPointCut"
           advice-ref="reinsAdvice" />
  </aop:config>

</beans>

4、数据库语句:

-- 创建aaa表用来验证增删改查的返回值
CREATE TABLE `reserve`.`aaa` (
 `uuid` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `systemcode` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,

 PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- 创建bbb表用来关联aaa的uuid作外键
CREATE TABLE `reserve`.`bbb` (
 `uuid` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `systemcode` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,

 PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

alter table bbb add constraint FK_T_POSITI_REFERENCE_T_COMPAN foreign key (uuid)references
aaa (uuid);

insert into bbb (uuid,systemcode,status)value ('1','2','2');

-- 验证事支持
DELETE from aaa where uuid != '1';
insert into aaa (uuid,systemcode,status)value ('2','2','2');
SELECT * FROM aaa;

排查过程共查找了下述方面:

1、排除数据库原因:

查看mysql数据库是支持事务的;而且用informix数据库进行了验证,同样没有回滚。

2、验证了impl的类型等均为问题。

3、查看了事务的配置信息也正确好用。

4、验证了系统其它的一些方法,发现是支持事务的。

5、将这两个语句放到其它方法里也好用。

6、事务是在service层处理的,在控制层也加了异常捕获(这个操作并不会影响事务回滚,即使不catch,也会回滚的)

最终锁定问题原因:是因为方法名称的问题。

当将方法名改成其它的,不以get开头,不报错。

这个问题很坑,因为本以为为配置文件中的get*,会使这个方法的事务起作用,谁知道恰恰get*的这个配置虽然起作用了,但是结果却是事务不回滚,在将该配置改为

<tx:method name="get*" propagation="SUPPORTS" rollback-for="java.lang.Exception"/>

也没有用,最后将其注释掉,事务回滚。走了下面的配置:

<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />

需要注意的是tx:method 的name属性指的是方法名。

将SUPPORTS改为REQUIRED后,事务也进行回滚。最终得到原因:是因为propagation的配置信息不正确。

拓展:

一、在声明式的事务处理中,要配置一个切面,其中就用到了propagation,表示打算对这些方法怎么使用事务,是用还是不用,其中propagation有七种配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默认是REQUIRED。

二、Spring中七种Propagation类的事务属性详解:

  • REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

三、注意.

这个配置将影响数据存储,必须根据情况选择。

问题往往出现在你忽略的地方。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot项目application.yml文件数据库配置密码加密的方法

    在Spring boot开发中,需要在application.yml文件里配置数据库的连接信息,或者在启动时传入数据库密码,如果不加密,传明文,数据库就直接暴露了,相当于"裸奔"了,因此需要进行加密处理才行. 使用@SpringBootApplication注解启动的项目,只需增加maven依赖 我们对信息加解密是使用这个jar包的: 编写加解密测试类: package cn.linjk.ehome; import org.jasypt.encryption.pbe.StandardP

  • Spring Junit单元测试加载配置文件失败问题

    JUnit是Java中最有名的单元测试框架,用于编写和运行可重复的测试,多数Java的开发环境都已经集成了JUnit作为单元测试的工具.好的单元测试能极大的提高开发效率和代码质量. 使用SpringJunit单元测试,通过@ContextConfiguration加载配置文件后,只会在src/test/resources目录下寻找配置文件,不会加载src/main/resources中的. 这样就导致了项目可以正常启动,但是单元测试时会提示找不到注入的类. 可以通过pom.xml配置来解决该问题

  • springboot配置文件的加载顺序解析

    这篇文章主要介绍了springboot配置文件的加载顺序解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 springboot启动时会扫描一下位置的application.properties或者application.yml文件作为默认配置文件: file:./config/ file:./ classpath:/config/ classpath:/ 以上是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级配置会覆盖低优先级配置

  • 关于springboot 配置文件中属性变量引用方式@@解析

    这种属性应用方式是 field_name=@field_value@. 两个@符号是springboot为替代${}属性占位符产生,原因是${}会被maven处理,所以应该是起不到引用变量的作用. @@方式可以引用springboot非默认配置文件(即其他配置文件)中的变量: springboot默认配置文件是 src/main/resources/application.properties 补充知识:springboot项目使用@Value注解获取配置文件中的配置信息 application

  • 基于SPRINGBOOT配置文件占位符过程解析

    这篇文章主要介绍了基于SPRINGBOOT配置文件占位符过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.配置文件占位符 1.application.properties server.port=8088 debug=false product.id=ID:${random.uuid} product.name=da mao mao product.weight=${random.int} product.fristLinePrice

  • Spring基于xml文件配置Bean过程详解

    这篇文章主要介绍了spring基于xml文件配置Bean过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 通过全类名来配置: class:bean的全类名,通过反射的方式在IOC容器中创建Bean,所以要求bean中必须有一个无参的构造器. <bean id="helloWorld" class="com.gong.spring.beans.HelloWorld"> <property na

  • springboot 使用yml配置文件给静态变量赋值教程

    声明: 此处需求是修改封装的clickhouseUtil数据查询引擎连接工具类.由于此类中的方法都是静态方法.连接地址等参数需要根据不同环境改变.例如开发下地址,测试下地址,生产地址等,所有通过配置文件来获取不同环境下的配置参数,但是使用的方法是静态的,所有不能使用一般情况下的@value直接给变量赋值,需要用到spring 属性的set方法来给静态变量赋值,然后静态方法使用静态变量即可 方法: 第一步:在yml文件中配置需要的参数 clickhouse: address: jdbc:click

  • SpringBoot如何读取配置文件参数并全局使用

    这篇文章主要介绍了SpringBoot如何读取配置文件参数并全局使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言: 读取配置文件参数的方法:@Value("${xx}")注解.但是@Value不能为static变量赋值,而且很多时候我们需要将参数放在一个地方统一管理,而不是每个类都赋值一次. 正文: 注意:一定要给类加上@Component 注解 application.xml test: app_id: 12345 app_

  • Spring事务管理配置文件问题排查

    在开发中,遇到了sql语句报错,但是并没有回滚的情况. 经过几天的排查,终于找到了事务没有回滚的原因. 原来的项目用的是informix的数据库,原来针对事务回滚的机制都是好用的.我本地用的是mysql数据库. 先将程序代码与spring-mybatis.xml配置文件拿过来: 1.程序代码: 这个问题是在验证增删改查返回值时发现的. 两个操作,删除时,因为关联了外键,所以会报错,此时正常情况更新的语句也会回滚,但是并没有. /** *@Author: Administrator on 2020

  • Spring事务管理原理及方法详解

    这篇文章主要介绍了Spring事务管理原理及方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 事务,在日常开发或者面试中都必定会涉及到.开发工作中,结合数据库开发理解就是:一组dml要么全部成功执行提交,要么因为某一个操作异常,撤销之前所做的成功的操作,整体执行失败.再简单点的一句话:生死与共. 由此,可以看出,事务的必要性:在开发工作中,保证操作数据的安全性.事务的控制也就是保证数据的访问安全性. 一.事务的四大特性 A:原子性(ato

  • Spring事务管理中关于数据库连接池详解

    目录 Spring事务管理 环境搭建 标准配置 声明式事务 总结 SqlSessionFactory XML中构建SqlSessionFactory 获得SqlSession的实例 代码实现 作用域(Scope)和生命周期 SqlSessionFactoryBuilder(构造器) SqlSessionFactory(工厂) SqlSession(会话) Spring事务管理 事务(Transaction),一般是指要做的或所做的事情.在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序

  • Spring事务管理详细讲解

    目录 事务回顾 spring事务操作 基于注解声明事务 @Transactional注解使用 事务传播机制 事务隔离级别 @Transactional其他属性 基于XML 声明式事务 完全注解开发 说明:基于atguigu学习笔记. 事务回顾 事务是逻辑上的一组数据库操作,要么都执行,要么都不执行. 假如,张三给李四转账100元,转账行为欧两个关键操作:将张三的余额减200元,将李四的余额增加200元.如果两个操作之间突然出现错误,例如银行系统崩溃导致张三余额减少,而李四的余额没有增加,这样的系

  • Spring事务管理的使用细则浅析

    目录 Spring 事务管理接口 1.事务管理器接口 PlatformTransactionManager 2.事务定义接口 TransactionDefinition Spring 事务管理的实现方法 1.没有事务管理的情况分析 2.通过配置 XML 实现事务管理 3.利用注解实现事务管理 4.在业务层实现事务管理   事务(Transaction)是访问数据库的一个操作序列,这些操作要么都做,要么都不做,是一个不可分割的工作单元.通过事务,数据库能将逻辑相关的一组操作绑定在一起,以便保持数据

  • Spring事务管理只对出现运行期异常进行回滚

    一.结论 Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚. 如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚. 关于异常的分类一下详细介绍: 1.基本概念 看java的异常结构图  Throwable是所有异常的根,java.lang.Throwable Error是错误,java.lang.Error Exception是异常,java.lang.Exception 2.Excep

  • Spring事务管理方法步骤解析

    1.Spring的事务管理主要包括3个接口 TransactionDefinition:封装事务的隔离级别,超时时间,是否为只读事务和事务的传播规则等事务属性,可通过XML配置具体信息. PlatformTransactionManager:根据TransactionDefinition提供的事务属性配置信息,创建事务. TransactionStatus:封装了事务的具体运行状态.比如,是否是新开启事务,是否已经提交事务,设置当前事务为rollback-only等. 2.Spring的事务管理

  • 解析spring事务管理@Transactional为什么要添加rollbackFor=Exception.class

    spring中事务处理原理 利用aop生成代理对象执行带有Transactional事务注解的方法业务逻辑.项目启动过程中会生成代理对象并将Transactional注解中的属性进行解析加载处理.在方法执行过程中如果出现异常,会根据注解配置决定是进入到事务回滚处理还是事务提交处理逻辑中,事务回滚处理逻辑中最终还是基于数据库的事务回滚处理. 异常的分类 案例说明 以自定义异常为例说明一下@Transactional中是否指定rollbackFor=Exception.class的区别     未指

  • Spring事务管理下synchronized锁失效问题的解决方法

    目录 一.我的思考 二.图解出现的原因 三.解决问题 总结 最近看到一个技术技术问题:synchronized锁问题? 开启10000个线程,每个线程给员工表的money字段[初始值是0]加1,没有使用悲观锁和乐观锁,但是在业务层方法上加了synchronized关键字,问题是代码执行完毕后数据库中的money 字段不是10000,而是小于10000 问题出在哪里? Service层代码: SQL代码(没有加悲观/乐观锁): 用1000个线程跑代码: 简单来说:多线程跑一个使用synchroni

  • Spring事务管理零基础入门

    目录 一.简介 二.特性(一原持久隔离) 2.1 原子性 2.2 一致性(类似能量守恒) 2.3 隔离性 2.4 持久性 三.隔离级别 3.1 事务级别(从低到高) 3.2 常用数据库默认级别: 3.3 事务中可能出现的问题: 四.传播特性 4.1 死活都不要事务 4.2 可有可无的 4.3 必须要有事务 五.应用 5.1 数据表 5.2 实体类 5.3 Service 一.简介 概念:事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,这些操作一起提交,要么都执行,要么都不

随机推荐