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"去掉后,事務就正常了

 

總結:我以前搭建過很多類似的框架,也就是把幾個框架整合在一起,很多配置也都是從已有的項目中照搬過來,大部分理解也都處理字面意思。所以,今天出現了這樣一個問題,一時半會也解決不了,最終在不服輸的態度下,還是解決了。所以,我的感受是,就像做數學題一樣,還是得多練習,才能在解決問題的速度上上一個層次。希望跟我遇到同樣困難的同學共勉,共同學習!

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM