<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    spring+hibernate連接泄漏之殤

    Posted on 2012-11-07 16:32 terryxue 閱讀(2279) 評論(0)  編輯  收藏 所屬分類: java
    數據庫連接泄漏是件可怕的事情,可能直接導致系統停止響應,另外因事務管理錯誤而導致數據出現不一致也是件可怕的事情,在后臺系統中,這兩個問題經常會結伴出現,本文將通過案例詳解使用Spring+Hibernate時可能導致問題的幾種情況,希望對大家有所幫助。

    文章比較長,如果你是遇到了問題正急求解決方案的話,可以先只讀這一條:檢查你的dao是否有直接使用session,修改為使用hibernateTemplate。
    如果你希望更多的了解session, hibernateTemplate, transaction,請繼續。

    以下案例基于Struts2.3.1+Spring3.1.1+hibernate3.5.4完成,案例場景為對系統參數進行管理,涉及如下四個類:
    Action PropertyAction
    Service PropertyService/PropertyServiceImpl
    DAO PropertyDAO(繼承HibernateDaoSupport)
    Entity SysProperty

    1. Action中直接調用dao,未使用getHibernateTemplate
    在一個復雜的多層架構系統中,事務控制在service中完成是更合理的,不應交由view層來控制,本文假定你是這么做的。一般我們代碼的處理順序為action->service->dao,然而總會有人會打破這種邏輯,打破可以,但要注意如下問題。
    假定Action中有如下代碼:
    1 @Autowired
    2 private PropertyDAO propertyDAO;
    3 @Override
    4 public String execute() throws Exception {
    5      model = propertyDAO.get(1L);
    6      return null;
    7 }

     這里action直接訪問了dao中的get方法,因事務配置在service層,因此這里是沒有事務控制的。
    接下來我們看dao中的get方法,假定你的定義如下:
    1 public SysProperty get(Long id) {
    2     return (SysProperty) getSession().get(SysProperty.class, id);
    3 }

    代碼很簡單,getSession是父類HibernateDaoSupport中提供的方法,這里直接通過實體Id get得到結果。
    接下來發布一下系統,訪問頁面,好像一切OK,但是,再刷新幾下看看,可能你的連接池配置的比較大,要刷新多次,最后你發會現你的系統停止了響應。更直接點,調用你采用的連接池中的相關API檢測下當前連接占用情況,你會發現刷新一次,被占用連接增加一個,可怕的事情發生了。
    小結:在無事務管理器控制的情況下,通過getSession()打開的session不會被關閉,這個session所占用的數據庫連接也不會被釋放。

    接下來,來點更可怕的,假設你的action中是這樣的:
    1 private List<Long> ids;
    2 @Override
    3 public String execute() throws Exception {
    4   for(Long id: ids) {
    5     results.add(propertyDAO.get(id));
    6   }
    7   return null;
    8 }
    9 
    這時嘗試一次傳入多個id進行請求,請求結束后檢查下連接數,你發現傳入多少ID,就有多少連接被占用,也就是說每一次對dao的get調用都會占用一個不可釋放的連接。
    小結:如果沒有配置事務管理器且直接使用getSession得到session,每次getSession都會創建一個新的session,每個session占用一個新的數據庫連接,session無線程級別的共享。

    2. Action中直接調用dao,使用getHibernateTemplate
    當然,你可能沒遇到過前面的情況,因為你會在DAO中這樣寫代碼:
    1 public SysProperty get(Long id) {
    2     return getHibernateTemplate().get(getEntityClass(), id);
    3 }
    那么是否就一切OK呢?如果只是想解決連接泄漏這個問題的話,答案是Yes,嘗試多次請求,檢查下你的連接池狀況,沒有連接泄漏的情況出現。所以簡單而可靠的辦法就是不再直接使用getSession,而是使用getHibernateTemplate進行相應操作,當需要使用session時,可以用下面的方法:
    getHibernateTemplate().execute(new HibernateCallback<T>() {
      
    public T doInHibernate(Session session) throws HibernateException {
        Query queryObject 
    = session.createQuery(hql);
        //....
      }
    });
    But...如果你的系統真的出現了連接泄漏,可能你需要關注更多。
    還是前面Action中根據ID循環查詢的操作,在這個案例中,每一次Dao的get方法都將重復這樣的邏輯:創建session,分配session一個連接,關閉session,釋放session占用的連接,也就是說session不會在線程級別共享。讓我們繼續,進入第三種情況來進一步說明。

    3. Service未合理配置事務管理器
    絕大部分情況下我們會給service配置事務管理器,可能是通過xml或是@Transactional注解,但并非配置了就OK了。看下面的例子:
    @Transactional
    public
     class PropertyServiceImpl implements PropertyService {
        @Autowired
        
    private PropertyDAO propertyDAO;
        
        @PostConstruct
        
    public void init(){
            propertyDAO.get(
    1L);
        }
        
    //
    }
    當然,假定你的DAO還是寫成了這樣:
    public SysProperty get(Long id) {
        
    return (SysProperty) getSession().get(SysProperty.class, id);
    }
    你期望在service初始化好后做一些數據庫操作,你也給service配置了事務管理器,接下來你啟動應用,然后檢查連接池中的連接數,你會發現有連接未被釋放!可能你會果斷的修改dao中的方法:
    public SysProperty get(Long id) {
        
    return getHibernateTemplate().get(getEntityClass(), id);
    }
    然后你會理所當然的認為,即使配置了事務管理器,依然不能使用getSession(),事實可能并非如此。
    我們調整一下代碼,不在init中調用dao中的get方法,改為如下:
    public SysProperty getProperty(Long id) {
        
    return propertyDAO.get(id);
    }
    然后DAO繼續使用(SysProperty) getSession().get(SysProperty.class, id),而action中的調用修改為:
    @Autowired
    private PropertyService propertyService;
    @Override
    public String execute() throws Exception {
        model 
    = propertyService.getProperty(1L);
        
    return null;
    }
    重新發布,調用,檢查連接數,發現無被占用連接存在。
    小結:如果正確配置了事務管理器,getSession是安全的。

    這時要清楚spring是通過AOP來實現事務控制的,而@PostConstruct方法不會受AOP控制,因此上面的init方法等于無事務管理器。
    那么再回頭來說,是否只要dao中使用getHibernateTemplate就不會有問題呢?
    假定service中的@PostConstruct方法如下:
    @PostConstruct
    public void init(){
        SysProperty p 
    = new SysProperty();
        p.setName(
    "test_property_1");
        propertyDAO.save(p);
    }
    強調一下,前面已經提到這個方法不受spring的事務管理器控制。
    假定DAO中的save方法如下:
    public void save(SysProperty o){
        getSession().save(o);
    }
    啟動一下應用,檢查一下連接數,再檢查一下數據是否有存儲到數據庫中。因為我們直接使用了getSession,因此連接不會釋放,這點前面已經提到,但同時我們還將發現數據沒有被存儲,當然這個也好理解,因為上面已經提到這個方法未配置事務管理器。
    小結:通過getSession().save()保存數據時,事務不會自動提交。

    現在再修改下DAO中的save方法:
    public void save(SysProperty o){
        getHibernateTemplate().save(o);
    }
    啟動一下應用,檢查一下連接數,再檢查一下數據是否有存儲到數據庫中。因為我們使用hibernateTemplate,因此連接有釋放,這點前面已經提到,但同時我們還發現數據也已存儲到數據庫中,說明hibernateTemplate會自動提交事務。
    小結:如果未配置事務管理器,通過hibernateTemplate操作時,會自動創建并提交事務。

    所以如果你覺得使用hibernateTemplate就OK了,那就要小心下面的代碼了:
    @PostConstruct
    public void init(){
      
    //1. 從你的賬號A中扣除一萬塊
      
    //2. 這里的代碼拋出了異常
      
    //3. 將你的賬號B中增加一萬塊 
    }
    如果上面的第2步出現了異常,那么因為1的事務已經提交,而3卻沒有執行,最終導致了數據的不一致,后果和連接泄漏一樣嚴重!
    除了@PostConstruct,還有其它原因會導致@Transactional無效,假定我們的service配置了事務管理器,但存在如下代碼:
    pubic void someServiceMethod(){
      
    new Thread(){
        
    public void run(){
            doSomethingLater();
        }
      }
    }

    public void doSomethingLater(){
      
    //做一系列數據庫操作
    }
    那么你可以去驗證下doSomethingLater是否受事務管理器控制,事實上并不會,所以你需要理解spring AOP的機制,否則一個小坑會釀成災難。
    這里還有一種情況,你不是在類上面配置@Transactional,而是在方法上面配置,假定存在如下的代碼:
    //該方法不需要事務控制
    public void method1(){
        method2();
    }

    //下面的方法需要事務控制
    @Transactional
    public void method2(){
        
    //do something
    }
    因為你需要給不同的方法配置不同的事務機制,因此你沒有在類上面進行配置,然后你在客戶端進行了如下調用:service.method1(); method2中的方法會受事務管理嗎?可悲的是并不會。
    上面講到的線程調用和內部方法調用可以這樣來處理:
    @Autowired
    private ApplicationContext context;  
    public void method1(){
        PropertyService service 
    = context.getBean(PropertyService.class);
        service.method2();
    }
    小結:注意spring的AOP機制

    4. Service合理配置事務管理器
    最后補充一下,如果事務管理器配置正確的話會發生什么。這時不管你是用getSession還是getHibernateTemplate,結果都是一樣,session將在thread級別共享,session只有一個。

    總結:難得這周開始工作變得清閑,上班時間還能寫寫博客,想想前段日子真是自己何苦為難自己。回到話題,使用getSession沒什么大錯,因為你本應正確配置事務管理器。使用hibernateTemplate能解決所有連接泄漏的問題,但要小心他可能隱藏的事務問題。另外就是spring中內部方法調用時AOP的問題,創建新線程時的事務問題。最后希望這篇有點繞的文章能給你帶來幫助。

    posts - 9, comments - 24, trackbacks - 0, articles - 0

    Copyright © terryxue

    主站蜘蛛池模板: 亚洲熟伦熟女新五十路熟妇| 国产免费久久精品| 亚洲邪恶天堂影院在线观看| 99久久成人国产精品免费| 亚洲午夜无码片在线观看影院猛| 99久久久精品免费观看国产| 亚洲人成图片小说网站| 国产线视频精品免费观看视频| 午夜视频免费在线观看| 亚洲精品乱码久久久久久 | 男人和女人高潮免费网站| 国产成人免费a在线视频app| 国产精品亚洲天堂| 亚洲一区二区三区在线播放 | 亚洲人成人无码网www电影首页| 亚洲自偷自拍另类12p| 无码国产精品一区二区免费模式| 女人18毛片水真多免费看| 亚洲精品无码人妻无码| 免费亚洲视频在线观看| 国产免费内射又粗又爽密桃视频 | 苍井空亚洲精品AA片在线播放| aa毛片免费全部播放完整| 在线观看亚洲天天一三视| 亚在线观看免费视频入口| 亚洲免费视频观看| 国产a不卡片精品免费观看| 中文日本免费高清| 亚洲天堂男人天堂| 成年女人毛片免费播放人| 黄床大片30分钟免费看| 久久国产亚洲精品麻豆| 久久经典免费视频| 一本久久免费视频| 麻豆亚洲av熟女国产一区二| 国产福利免费观看| 日韩精品内射视频免费观看| 亚洲av无码日韩av无码网站冲 | 天天影院成人免费观看| 精品亚洲视频在线| 亚洲AV无码成人专区片在线观看|