• 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:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 

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

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

    还是对spring的各种配置不太熟悉。

    花费了太多时间。

    如果错过太阳时你流了泪,那你也要错过群星了。
    在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
    不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。
  • 相关阅读:
    Android应用安全之外部动态加载DEX文件风险
    Android应用安全之Android APP通用型拒绝服务漏洞
    利用Cydia Substrate进行Android HOOK(二)
    Apk去签名校验详解
    nginx部署前端项目的一些配置【刚入门】
    java 日期格式化 将String日期重新格式化成String型【转】
    idea导入maven项目时需要注意
    element 'dependency' cannot have character [children], because the type's content type is element-only.
    腾讯云centos7执行 systemctl status postfix.service 启动postfix服务时出现 /usr/sbin/postconf: fatal: parameter inet_interfaces: no local interface found for ::1
    redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource since the pool is exhausted
  • 原文地址:https://www.cnblogs.com/szrs/p/12910036.html
Copyright © 2020-2023  润新知