皮膚 顏色
板式
寬屏 窄屏

Spring事務(wù)管理--數據連接泄漏

發(fā)表日期:2015-09-16    文章編輯:本站編輯   來(lái)源:網(wǎng)絡(luò )    瀏覽次數:

看下面的代碼,其中連接池采用的c3p0,配置文件省略

import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;



@Service(value="jdbcSpring")
public class JdbcSpring implements IJdbcSpring {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jt;

   
    /* (non-Javadoc)
     * @see sping.jdbc.IJdbcSpring#addPerson()
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRED)//3

    public void addPerson(){
        try {

           Connection con=jt.getDataSource().getConnection();// 1,獲取連接

            jt.execute("insert into person(pname) values ('哈哈')");      

    } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
      
    }

}

此代碼在1處顯示的從數據源里獲取一條連接,而在程序最后并沒(méi)有關(guān)閉釋放這個(gè)連接,則這個(gè)連接一直處于被暫用狀態(tài),造成了連接的泄露。我們寫(xiě)一段junit測試代碼,其中的測試方法為

package sping.jdbc.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import sping.jdbc.IJdbcSpring;

public class SpringJdbcTest {
    private static ApplicationContext context;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        context = new ClassPathXmlApplicationContext(
                new String[] { "jdbc.xml" });
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Test
    public void test() {
        try {
            ComboPooledDataSource cpds = context.getBean("dataSource",
                    ComboPooledDataSource.class);
            IJdbcSpring jtm = context.getBean("jdbcSpring", IJdbcSpring.class);
            for (int i = 0; i < 10; i++) {

              jtm.addPerson();// 2

                System.out.println("---鏈接總數量" + cpds.getNumConnections()
                        + "使用中的鏈接" + cpds.getNumBusyConnections() + "空閑的鏈接"
                        + cpds.getNumIdleConnectionsAllUsers());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
我們在2處調用addPerson()方法,讓我們來(lái)運行程序看一下結果

---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接3空閑的鏈接0
---鏈接總數量6使用中的鏈接4空閑的鏈接2
---鏈接總數量6使用中的鏈接5空閑的鏈接1
---鏈接總數量6使用中的鏈接6空閑的鏈接0
---鏈接總數量9使用中的鏈接7空閑的鏈接2
---鏈接總數量9使用中的鏈接8空閑的鏈接1
---鏈接總數量9使用中的鏈接9空閑的鏈接0
---鏈接總數量12使用中的鏈接10空閑的鏈接2
---鏈接總數量12使用中的鏈接11空閑的鏈接1

我們可以看到連接的總數在不斷的增加,使用的連接也在不斷的增加,這說(shuō)明有連接泄露,

 

原因是直接通過(guò)數據源獲取連接(jdbcTemplate.getDataSource().getConnection())而沒(méi)有顯式釋放造成的。當然你可以手動(dòng)去顯示的釋放連接。這就多多少少的改為手工管理了,有點(diǎn)和spring事務(wù)管理機制相違背的味道。

那么,應該怎么樣從數據源中往外取連接呢?,通過(guò)DataSourceUtils 來(lái)獲取連接,看下面的代碼

 我們先來(lái)看一下org.springframework.jdbc.datasource.DataSourceUtils中重要的方法

/**
     * Actually obtain a JDBC Connection from the given DataSource.
     * Same as {@link #getConnection}, but throwing the original SQLException.
     * <p>Is aware of a corresponding Connection bound to the current thread, for example
     * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread
     * if transaction synchronization is active (e.g. if in a JTA transaction).
     * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.
     * @param dataSource the DataSource to obtain Connections from
     * @return a JDBC Connection from the given DataSource
     * @throws SQLException if thrown by JDBC methods
     * @see #doReleaseConnection
     */
    public static Connection doGetConnection(DataSource dataSource) throws SQLException
      首先嘗試從事務(wù)上下文中獲取連接,失敗后再從數據源獲取連接

    public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
        try {
            return doGetConnection(dataSource);
        }
        catch (SQLException ex) {
            throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
        }
    }

可見(jiàn)此方法內部實(shí)現就是用doGetConnection(DataSource dataSource)方法來(lái)實(shí)現的,功能和上面的方法一樣。

public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException

此方法為釋放連接,放回連接池中

public static void releaseConnection(Connection con, DataSource dataSource) {
        try {
            doReleaseConnection(con, dataSource);
        }
        catch (SQLException ex) {
            logger.debug("Could not close JDBC Connection", ex);
        }
        catch (Throwable ex) {
            logger.debug("Unexpected exception on closing JDBC Connection", ex);
        }
    }

此方法就是通過(guò)上面的方法實(shí)現的,所以功能也一樣,釋放連接,歸還到連接池

我們現在用DatasourceUtils來(lái)獲取連接

我們只需要將代碼1處獲取連接的代碼替換成

DataSourceUtils.getConnection(jt.getDataSource());

運行程序,控制臺程序輸出

---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2

說(shuō)明已經(jīng)沒(méi)有連接泄露了,我們通過(guò)DataSourceUtils的
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException

這個(gè)方法來(lái)獲取連接,并沒(méi)有顯示的釋放連接,連接并沒(méi)有泄露。

那么,我們使用DataSourceUtils工具類(lèi)來(lái)獲取連接是否就非常的安全了呢?答案是否定的。

因為通過(guò)DataSourceUtils工具類(lèi)在沒(méi)有事務(wù)上下文的方法中獲取連接使用,也會(huì )造成連接泄露。

我們從配置文件xml中刪除事務(wù)的配置。本來(lái)不想貼xml配置了,還是貼上吧,

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-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 ">


    <context:component-scan base-package="sping.jdbc*"></context:component-scan>

    <!-- 數據源默認將autoCommit設置為true -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>
<!--這里是事務(wù)配置,-->
<!--
    <bean id="myTxManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" >
        </property>
    </bean>
    <tx:annotation-driven transaction-manager="myTxManager" />
-->
    <context:property-placeholder location="jdbc.properties" />
</beans>

再次運行程序,輸出如下

---鏈接總數量3使用中的鏈接2空閑的鏈接1
---鏈接總數量3使用中的鏈接3空閑的鏈接0
---鏈接總數量6使用中的鏈接4空閑的鏈接2
---鏈接總數量6使用中的鏈接5空閑的鏈接1
---鏈接總數量6使用中的鏈接6空閑的鏈接0
---鏈接總數量9使用中的鏈接7空閑的鏈接2
---鏈接總數量9使用中的鏈接8空閑的鏈接1
---鏈接總數量9使用中的鏈接9空閑的鏈接0
---鏈接總數量12使用中的鏈接10空閑的鏈接2
---鏈接總數量12使用中的鏈接11空閑的鏈接1

數據連接有開(kāi)始泄露來(lái)了!那么我們應該怎么做呢。要想避免連接泄露,我們須手動(dòng)去關(guān)閉連接了,通過(guò)DataSourceUtils工具類(lèi)

改造addPerson()方法,加上finally,如

    public void addPerson() {
        Connection con = null;
        try {
            // con=jt.getDataSource().getConnection();
            con = DataSourceUtils.getConnection(jt.getDataSource());

            jt.execute("insert into person(pname) values ('王江濤')");
        } catch (Exception e) {

        } finally {
            DataSourceUtils.releaseConnection(con, jt.getDataSource());
        }
    }

再次運行程序,結果如下
---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接1空閑的鏈接2
---鏈接總數量3使用中的鏈接2空閑的鏈接1
---鏈接總數量3使用中的鏈接2空閑的鏈接1
---鏈接總數量3使用中的鏈接2空閑的鏈接1
---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接2空閑的鏈接2
---鏈接總數量3使用中的鏈接2空閑的鏈接2

我們看到連接數量好像不對稱(chēng)了,最后一行,使用的2,空閑的2,而連接總數為3,具體什么原因我沒(méi)有去深究,我的猜想是連接回收時(shí)有延遲, 而回收連接結束正好是再4處完成,所以空閑數量就增加了1,所以 造成了連接的不對稱(chēng)?

System.out.println("---鏈接總數量" + cpds.getNumConnections()+ "使用中的鏈接" + cpds.getNumBusyConnections() +"4"+"空閑的鏈接"+ cpds.getNumIdleConnectionsAllUsers());

不同數據訪(fǎng)問(wèn)框架 DataSourceUtils 的等價(jià)類(lèi)
數據訪(fǎng)問(wèn)技術(shù)框架 連接 ( 或衍生品 ) 獲取工具類(lèi)
Spring JDBC org.springframework.jdbc.datasource.DataSourceUtils
Hibernate org.springframework.orm.hibernate3.SessionFactoryUtils
iBatis org.springframework.jdbc.datasource.DataSourceUtils
JPA org.springframework.orm.jpa.EntityManagerFactoryUtils
JDO org.springframework.orm.jdo.PersistenceManagerFactoryUtils

相關(guān)文章推薦

  
Socket 和 WebSocket 有哪些區別和聯(lián)系? WebSocket 和 HTML5 是什么關(guān)系? 必須在瀏覽器中才能使用...
  
其實(shí)對于我們一般理解的計算機內存,它算是CPU與計算機打交道最頻繁的區域,所有數據都是...
  
簡(jiǎn)介 響應式Web設計 是一種創(chuàng )建Web應用程序的新方法。一旦采用 響應式Web設計 創(chuàng )建出應用程序...
  
用div做成表格的形式,把標簽中間的空格都去掉就可以了...
  
看下面的代碼,其中連接池采用的c3p0,配置文件省略 import java.sql.Connection; import org.springframe...
  
主要幾個(gè)框架或者插件是如何實(shí)現異步加載事件響應的。 一.LABjs 這個(gè)項目位于github上面,其本...
  
html5shiv讓IE6-IE8支持HTML5標簽 越來(lái)越多的站點(diǎn)開(kāi)始使用 HTML5 標簽。但是目前的情況是還有很多人...
  
緩存 是實(shí)際工作中非常常用的一種提高性能的方法, 我們會(huì )在許多場(chǎng)景下來(lái)使用緩存。 本文通...
  
為了防止惡意用戶(hù)發(fā)布惡意內容,我們的安全分析瀏覽器都在虛擬機上運行。這使我們能夠確...
?