空間站

          北極心空

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
          ?????????????????????? ?????? 事務管理最佳實踐多余的話之三
          ????????????? Spring 聲明式事務管理出錯示例與解決之道
          ?
          ?
          ?
          前言
          今天,發(fā)現(xiàn)了一個以前寫的使用Spring聲明式事務管理的程序爆出了數(shù)據(jù)庫連接錯誤,感覺是非常典型的一個誤用Spring聲明式事務管理的例子,拿出來為大家點評一下。
          ?
          ?
          Spring聲明式事務管理出錯示例
          這個應用程序是使用Spring管理的iBatis程序。事務使用了Spring的聲明式事務管理。
          爆出了如下的錯誤:
          Caused by:
          java.sql.SQLException : Connection is read-only. Queries leading to data modification are not allowed
          ?
          at com.withub.wcms.manage.collectnews.systemNewsinfo.dao.WcmsSystemNewsinfoDao.add( WcmsSystemNewsinfoDao.java:50 )
          ??? at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.saveOrUpdate( WcmsSystemNewsinfoService.java:103 )
          ??? at com.withub.wcms.manage.collectnews.systemNewsinfo.service.WcmsSystemNewsinfoService.formatRawInfoToHtml( WcmsSystemNewsinfoService.java:89 )
          ?
          可以看出,出現(xiàn)的錯誤是,Spring管理下的iBatis使用的數(shù)據(jù)庫連接是只讀的。而在DAO類的方法中,卻使用了修改數(shù)據(jù)庫的iBatis方法。
          ?
          源代碼:

          WcmsSystemNewsinfoDao類的源代碼就不列出來了。這個Dao類的add方法使用了iBatis的update方法,更新數(shù)據(jù)庫數(shù)據(jù)。
          下面是WcmsSystemNewsinfoService類的部分相關(guān)調(diào)用方法的源代碼:
          public?String?formatRawInfoToHtml(String?infoRawId)?throws?Exception{
          ???????WcmsSystemNewsinfoRawModule?wcmsSystemNewsinfoRawModule
          =new?WcmsSystemNewsinfoRawModule();
          ???????wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);
          ???????
          //原表數(shù)據(jù)
          ????????wcmsSystemNewsinfoRawModule=this.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);
          ???????WcmsSystemNewsinfoModule?wcmsSystemNewsinfoModule
          =new?WcmsSystemNewsinfoModule();
          ???????
          //目標表數(shù)據(jù)
          ???????BeanUtils.copyProperties(wcmsSystemNewsinfoModule,?wcmsSystemNewsinfoRawModule);
          ???????
          ???????wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());
          ???????wcmsSystemNewsinfoModule.setInfoId(
          null);
          ???????
          ???????returnthis.saveOrUpdate(wcmsSystemNewsinfoModule);
          ???????
          ????}
          ????
          /**
          ?????*需要把目標表Model信息保存在目標表中。如果該記錄已存在,則更新,否則,新增!
          ?????*
          ?????*@parammodule
          ?????*@throwsException
          ?????
          */
          ????
          public?String?saveOrUpdate(WcmsSystemNewsinfoModule?module)?throws?Exception{
          ???????WcmsSystemNewsinfoModule?loadModule
          =this.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());
          ???????String?infoId
          =null;
          ???????
          if(loadModule==null){
          ???????????
          //插入
          ???????????infoId=this.getWcmsSystemNewsinfoDao().add(module);
          ???????????
          ???????}
          else{
          ???????????
          //更新
          ???????????/**
          ????????????*更新
          ????????????*1,找到主鍵條件
          ????????????
          */
          ???????????infoId
          =loadModule.getInfoId();
          ???????????module.setInfoId(loadModule.getInfoId());
          ???????????
          this.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);
          ???????????
          ???????}
          ???????
          return?infoId;
          ????}
          源代碼說明:
          WcmsSystemNewsinfoService類的formatRawInfoToHtml(String infoRawId)方法,根據(jù)傳入的參數(shù),準備好所需的數(shù)據(jù),然后調(diào)用本類的saveOrUpdate(WcmsSystemNewsinfoModule module)方法。
          saveOrUpdate()方法,根據(jù)情況,調(diào)用WcmsSystemNewsinfoDao類的add或者update方法。
          ?
          Spring事務配置文件
          一、事務配置抽象Bean聲明

          <bean?id="txProxyTemplate"?abstract="true"?class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          ???????
          <property?name="transactionManager"?ref="transactionManager"/>
          ???????
          <property?name="transactionAttributes">
          ???????????
          <props>
          ??????????????
          <prop?key="*">readOnly</prop>
          ??????????????
          <prop?key="add*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="save*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="modify*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="update*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="delete*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="remove*">PROPAGATION_REQUIRED,-Exception</prop>
          ??????????????
          <prop?key="query*">PROPAGATION_REQUIRED,?readOnly,-Exception</prop>
          ??????????????
          <prop?key="load*">PROPAGATION_REQUIRED,?-Exception</prop>
          ???????????
          </props>
          ???????
          </property>
          ????
          </bean>


          二、服務類的Spring聲明式事務管理

          <!--?
          ????wcmsSystemNewsinfoService
          ?????
          -->
          ????
          <bean?id="wcmsSystemNewsinfoService"
          ???????parent
          ="txProxyTemplate">
          ???????
          <property?name="target">
          ???????????
          <ref?bean="wcmsSystemNewsinfoServiceTarget"/>
          ???????
          </property>
          ????
          ????
          </bean>


          錯誤解析
          錯誤的原因就在上面的Spring聲明式事務里。執(zhí)行WcmsSystemNewsinfoService類的formatRawInfoToHtml()方法時,會應用txProxyTemplate的配置,由于它的方法名與所有的特殊配置都不匹配,因此,會應用第一個聲明式事務:
          ??? <prop key="*">readOnly</prop>
          因此,SpringAOP創(chuàng)建了一個只讀的數(shù)據(jù)庫連接和事務。
          然后,調(diào)用WcmsSystemNewsinfoService類的saveOrUpdate()方法,也會查找上面的事務配置,匹配:
          <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
          SpringAOP會去得到數(shù)據(jù)庫連接和設置事務。由于在本地線程變量中已經(jīng)找到了前面提供的只讀連接,就會直接使用這個數(shù)據(jù)庫連接,并在其上設置事務。
          最后,調(diào)用WcmsSystemNewsinfoDao類的add方法。由于使用的是只讀連接,執(zhí)行add方法中的update語句,就發(fā)生了上面的錯誤:
          Caused by:
          java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
          ?
          ?
          Spring真的能夠管理一切嗎?
          像上面這樣的Spring事務配置和Service、Dao的寫法,應當說是相當普遍的,我們認為Spring已經(jīng)把數(shù)據(jù)庫連接、事務、O-R映射等等管得妥妥當當了,不用我們再操心了,再理解事務了!
          真的如此嗎?錯!
          漠視數(shù)據(jù)庫、漠視事務,我們的系統(tǒng)到底有多少類似的隱患?我們的業(yè)務服務類Service浪費了多少SPringAOP的幫助?降低了多少性能?
          Spring、EJB這樣的聲明式事務,確實大大方便了我們處理數(shù)據(jù)庫連接和事務。但是,我們還是需要自己理解業(yè)務邏輯對數(shù)據(jù)庫連接,對事務的需要!
          工具只能幫助我們解決我們認識到的問題,解決不了我們都沒理解的問題。
          ?
          ??? 不能再把一切扔給框架、容器、工具!首先理解你的業(yè)務邏輯,理解你要實現(xiàn)的功能,然后搞清楚框架、容器、工具會幫助我們做什么。只有理解了自己的業(yè)務邏輯,理解了自己的代碼,理解了自己要用到的第三方代碼,才能真正完美地實現(xiàn)我們需要的功能!
          ?
          用我們的命名方法來重構(gòu)上面的問題代碼
          一、給Service類中的2個方法加上后綴名標識對事務的依賴
          ??? 當然,Service接口相應的方法也要改變。

          /*?(non-Javadoc)
          ?????*?@see?com.withub.wcms.manage.collectnews.systemNewsinfo.service.IWcmsSystemNewsinfoService#formatRawInfoToHtml(java.lang.String)
          ?????
          */
          ????
          public?String?formatRawInfoToHtmlTransaction(String?infoRawId)?throws?Exception{
          ???????WcmsSystemNewsinfoRawModule?wcmsSystemNewsinfoRawModule
          =new?WcmsSystemNewsinfoRawModule();
          ???????wcmsSystemNewsinfoRawModule.setInfoId(infoRawId);
          ???????
          //原表數(shù)據(jù)
          ????????wcmsSystemNewsinfoRawModule=this.getManageNewsinfoService().queryRawInfoById(wcmsSystemNewsinfoRawModule);
          ????
          ???????WcmsSystemNewsinfoModule?wcmsSystemNewsinfoModule
          =new?WcmsSystemNewsinfoModule();
          ???????
          //目標表數(shù)據(jù)
          ???????BeanUtils.copyProperties(wcmsSystemNewsinfoModule,?wcmsSystemNewsinfoRawModule);
          ???????
          ???????wcmsSystemNewsinfoModule.setInfoRawId(wcmsSystemNewsinfoRawModule.getInfoId());
          ???????wcmsSystemNewsinfoModule.setInfoId(
          null);
          ???????
          ???????returnthis.saveOrUpdateDao(wcmsSystemNewsinfoModule);
          ???????
          ????}
          ????
          /**
          ?????*需要把目標表Model信息保存在目標表中。如果該記錄已存在,則更新,否則,新增!
          ?????*
          ?????*@parammodule
          ?????*@throwsException
          ?????
          */
          ????
          public?String?saveOrUpdateDao(WcmsSystemNewsinfoModule?module)?throws?Exception{
          ???????WcmsSystemNewsinfoModule?loadModule
          =this.getWcmsSystemNewsinfoDao().selectWcmsSystemNewsinfoModuleByInfoRawId(module.getInfoRawId());
          ???????String?infoId
          =null;
          ???????
          if(loadModule==null){
          ???????????
          //插入
          ???????????infoId=this.getWcmsSystemNewsinfoDao().add(module);
          ???????????
          ???????}
          else{
          ???????????
          //更新
          ???????????/**
          ????????????*更新
          ????????????*1,找到主鍵條件
          ????????
          */
          ???????????infoId
          =loadModule.getInfoId();
          ???????????module.setInfoId(loadModule.getInfoId());
          ???????????
          this.getWcmsSystemNewsinfoDao().updateProcessedInfo(module);
          ???????????
          ???????}
          ???????
          return?infoId;
          ????}
          這樣,formatRawInfoToHtmlTransaction方法可以直接被最終用戶調(diào)用。它將能夠創(chuàng)建或得到數(shù)據(jù)庫連接,管理事務,最后關(guān)閉數(shù)據(jù)庫連接。
          而,saveOrUpdateDao方法,不能直接被最終用戶調(diào)用。如果它需要數(shù)據(jù)庫連接,它可以使用本地線程變量中保存的數(shù)據(jù)庫連接。
          ?
          二、修改Service類的聲明式事務的配置文件

          <!--?
          ????wcmsSystemNewsinfoService
          ?????
          -->
          ????
          <bean?id="wcmsSystemNewsinfoService"
          ???????parent
          ="txProxyTemplate">
          ???????
          <property?name="target">
          ???????????
          <ref?bean="wcmsSystemNewsinfoServiceTarget"/>
          ???????
          </property>
          ???????
          <property?name="transactionAttributes">
          ???????????
          <props>
          ??????????????
          <prop?key="*Transaction">PROPAGATION_REQUIRED,-Exception</prop>
          ???????????
          </props>
          ???????
          </property>
          ????
          ????
          </bean>


          這里,重載了txProxyTemplate的聲明式事務配置。我們只對Service類中的以Transaction結(jié)尾的方法,應用事務,在發(fā)生異常時,回滾。
          這樣,Transaction方法直接或者間接調(diào)用的DAO接口中的方法,就可以使用本地線程變量中保存的由Transaction方法的AOP代理方法創(chuàng)建的數(shù)據(jù)庫連接。
          數(shù)據(jù)庫連接和事務被真正的擺平了!世界真美好!
          ?
          ?
          ?
          ?
          ?

          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1423460

          posted on 2006-12-02 23:33 蘆葦 閱讀(878) 評論(0)  編輯  收藏 所屬分類: Spring
          主站蜘蛛池模板: 辛集市| 建平县| 盈江县| 翁源县| 黔江区| 桦南县| 镇巴县| 六盘水市| 曲麻莱县| 阜新| 泗水县| 灵武市| 新乡县| 张北县| 永兴县| 拉萨市| 白水县| 滦平县| 建平县| 吉林省| 刚察县| 吕梁市| 桓台县| 广丰县| 隆化县| 达拉特旗| 灵石县| 华池县| 剑河县| 江西省| 浦江县| 库车县| 怀集县| 太仆寺旗| 江华| 义马市| 东城区| 连江县| 绥德县| 汾西县| 临武县|