zoukankan      html  css  js  c++  java
  • hibernate4 spring3.2 事务不提交分析

      最近在做微信项目,我搭建了一个基于servlet,spring3.2,hibernate4.1的框架。因为基于消息的servlet和基于业务层是分开做的,也就是先把业务层做了,再去将所有的请求转到业务层处理。所以一开始开发就用junit做测试,模拟的消息保存数据库也都能正常进行。下面列出某一个junit 的 testcase,在这个测试的例子中,我为junit配置了事务,事务也能正常提交。所以,后面的业务层写法就一直这么开发下去,也没什么问题

      

    package com.cpic.inf.tools.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.transaction.TransactionConfiguration;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.alibaba.fastjson.JSONArray;
    import com.cpic.inf.model.json.JaoFeiJson;
    import com.cpic.inf.service.SendModelMsgProxy;
    import com.cpic.inf.tools.exception.SendException;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring_*.xml"})
    @Transactional
    @TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)//true:始终回滚 false:数据提交
    public class TestCase {
        
        @Autowired
        SendModelMsgProxy proxy;
        
        @Test
        public void testModelMsg() throws SendException{
            JaoFeiJson jaofei = new JaoFeiJson();
            jaofei.setAccount("62260****4765");
            jaofei.setAmount("200元");
            jaofei.setContno("张三");
            jaofei.setFinish("2014年1月30日");
            jaofei.setPaycode("交通银行");
            jaofei.setPeriod("第3期");
            jaofei.setPolicyno("201400000009");
            jaofei.setProduct("鸿发年年及其附加险");
            jaofei.setRemark("如有疑问,请在”更多--小薇在线“里留言~");
            jaofei.setTemplate_id("4g3_jAn3qPqhX6DR2TbU1r9-rDN2N4KazEIPLhQ3FKQ");
            jaofei.setTitle("您好,您的保单续期交费成功啦~");
            //jaofei.setTopcolor("#04B404");
            jaofei.setTouser("ox6yJjtSe02C6b3I_Fues2WPYszk");
            jaofei.setUrl("http://www.baidu.com");
            String str = JSONArray.toJSON(jaofei).toString();
            proxy.sendMsg(str);
        }
    }

    后面当业务层开发完后,将工程放到web容器里跑,发送一个请求,调用到业务层方法,前面一直正常,后面发现一个问题,事务始终不提交,先看我的棕spring部分配置

    <!-- 开启AOP监听 只对当前配置文件有效 -->
        <aop:aspectj-autoproxy expose-proxy="true"/>
        
        <!-- 开启注解事务 只对当前配置文件有效 -->
          <tx:annotation-driven transaction-manager="txManager"/>
    
        <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
        <tx:advice id="txAdvice" transaction-manager="txManager">
            <tx:attributes>
                <tx:method name="save*" propagation="REQUIRED" />
                <tx:method name="add*" propagation="REQUIRED" />
                <tx:method name="create*" propagation="REQUIRED" />
                <tx:method name="insert*" propagation="REQUIRED" />
                <tx:method name="update*" propagation="REQUIRED" />
                <tx:method name="merge*" propagation="REQUIRED" />
                <tx:method name="del*" propagation="REQUIRED" />
                <tx:method name="remove*" propagation="REQUIRED" />
                <tx:method name="put*" propagation="REQUIRED" />
                <tx:method name="use*" propagation="REQUIRED"/>
                <!--hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到-->
                <tx:method name="get*" propagation="REQUIRED" read-only="true" />
                <tx:method name="count*" propagation="REQUIRED" read-only="true" />
                <tx:method name="find*" propagation="REQUIRED" read-only="true" />
                <tx:method name="list*" propagation="REQUIRED" read-only="true" />
                <tx:method name="send*" propagation="REQUIRED" read-only="true"/>
                <tx:method name="*"  read-only="true" />
            </tx:attributes>
        </tx:advice>
        <aop:config expose-proxy="true">
            <!-- 只对业务逻辑层实施事务 -->
            <aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
            <!-- 自定义前置通知 -->
            <!--  <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>  -->
        </aop:config>
        
        <bean id="methodAdvice" class="com.cpic.inf.service.impl.MethodBefore"></bean>

     事务配置是比较清楚的,配置了aspectj的aop做为事务,我想要在所有的impl层的类里加上配置的事务,其中我现在测试的类是com.cpic.inf.service.impl.JaoFeiImpl

    来看一下这个类的实现

    package com.cpic.inf.service.impl;
    
    import java.util.Map;
    
    import org.apache.log4j.Logger;
    
    import com.alibaba.fastjson.JSONArray;
    import com.cpic.inf.model.BaseValue;
    import com.cpic.inf.model.RequestModel;
    import com.cpic.inf.model.in.JaoFeiModel;
    import com.cpic.inf.model.mapper.JiaoFeiModelDB;
    import com.cpic.inf.service.SendModelMsgABS;
    import com.cpic.inf.tools.Token;
    import com.cpic.inf.tools.exception.SendException;
    
    /**
     * 交费成功通知
     * @author wuxw
     *
     */
    public class JaoFeiImpl extends SendModelMsgABS  {
        
        private static Logger logger = Logger.getLogger(Token.class);
        
        /** 模版顶部边框颜色*/
        public static final String TOP_COLOR = "#04B404";
        
        /** 模版字体颜色*/
        public static final String COLOR = "#000000";
    
        public String sendMsg(Map<String,String> map) throws SendException {
            String token;
            try {
                token = Token.getToken();
            } catch (Exception e1) {
                throw new SendException("无法连接Token服务器");
            }
            RequestModel<JaoFeiModel> reqModel = null;
            try{
                ...
                
                try{
                    this.save(jiaofei);
                } catch(Exception e){
                    throw new SendException("数据库错误:" + e.getMessage());
                }
            } catch (Exception e) {
                throw new SendException("模板参数错误!请填写正确的模板对应参数");
            }
            String json = JSONArray.toJSONString(reqModel);
            try {
                logger.info("jiaofei end");
                return this.SendModelMsg(json, token);
            } catch (Exception e) {
                throw new SendException("网络异常,无法连接微信服务器");
            }
        }
    }

     按照我的逻辑,如果我调用这个sendMsg方法,就能够发送模板消息(注:这是一个发送微信模板消息的功能),并将这个模板消息存到数据库里。另外,用junit测试过,这个功能不存在逻辑错误,能够在junit下,发送消息成功后能正常保存到数据库里。但后面在web工程里,面前都能成功,直到this.save(jiaofei);的时候,没有看到hibernate打印出sql(show_sql=true);也就是事务没提交,为什么会这样了?

    我第一反映是

    <aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />

    这块是不是配置错了

    在网上查了一下,这个表达式怎么写,发现没有写错,我如果改成其他目录的时候

    this.save(jiaofei);在执行这句的时候,会报得不到session,也就是表明aop配置是正确的。

    因为我的方法是sendMsg方法,事务里没有配置这个,所以我又在配置里加上了这句

    <tx:method name="send*" propagation="REQUIRED" read-only="true"/>

    运行的时候还是跟以前的一样,逻辑上我是没问题,但一直找不到问题出在哪里,后面在网上找了个投机的办法

    在session.save();的后面加一句session.flush();

    这下子,事务能正确提交了,问题暂时是解决了,因为当时数据库操作只用到了save()方法。接着赶进度。

    但做为一个严谨的程序,我知道这是不对的,因为我需要的是aop来自动管理事务,而不是现在这样,操作一次就提交一次,要是遇到事务传播,这种方法就不奏效了,所以在空闲的时候,我把这个问题仔细查找一下原因。

    后面我配置了一个前置消息,配置在aop中,配置是这样的

      <aop:config expose-proxy="true">
            <!-- 只对业务逻辑层实施事务 -->
            <aop:pointcut id="txPointcut" expression="execution(* com.cpic..service.*.*(..))" />
            <!-- 自定义前置通知 -->
           <aop:advisor advice-ref="methodAdvice" pointcut-ref="txPointcut"/>
        </aop:config>
        
        <bean id="methodAdvice" class="com.cpic.inf.service.impl.MethodBefore"></bean>


    当我执行到sendMsg()方法之前,是先进入我的前置消息的,也就是证明,不是aop的原因,事务不提交肯定是配置出了问题,我后面在这其他业务类里加个@Transactional注解,事务也提交了,所以我猜,肯定是配置出了问题,我仔细检查,发现问题出现在

    <tx:method name="send*" propagation="REQUIRED" read-only="true"/>

    我仔细查了查这里面每个属性的意思,因为我的理解也都只在单词字面意思上

    propagation="REQUIRED":指事务传翻

    read-only="true":指只读情况下,不会有事务

    所以,我把read-only="true"去掉后,事务就正常了

    总结:我以前搭建过很多类似的框架,也就是把几个框架整合在一起,很多配置也都是从已有的项目中照搬过来,大部分理解也都处理字面意思。所以,今天出现了这样一个问题,一时半会也解决不了,最终在不服输的态度下,还是解决了。所以,我的感受是,就像做数学题一样,还是得多练习,才能在解决问题的速度上上一个层次。希望跟我遇到同样困难的同学共勉,共同学习!

  • 相关阅读:
    HTTP协议
    idea新建工程项目结构
    idea使用的JDK版本1.9换成1.8后相关的更改设置
    Servlet
    Tomcat三种项目部署方式
    Tomcat环境变量配置命令行报错:The JRE_HOME environment variable is not defined correctl This environment variable is needed to run this program
    JDBC面试题
    XML基础入门
    数据库连接池——Druid
    $.ajax 分页
  • 原文地址:https://www.cnblogs.com/wxwall/p/3761233.html
Copyright © 2011-2022 走看看