spring 事務管理 3(數據鏈接泄漏)

看下面的代碼,其中鏈接池採用的c3p0,配置文件省略java

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()
     */
    @Overridespring

    @Transactional(propagation=Propagation.REQUIRED)//3

    public void addPerson(){
        try {sql

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

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

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

}框架

此代碼在1處顯示的從數據源裏獲取一條鏈接,而在程序最後並無關閉釋放這個鏈接,則這個鏈接一直處於被暫用狀態,形成了鏈接的泄露。咱們寫一段junit測試代碼,其中的測試方法爲ide

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()方法,讓咱們來運行程序看一下結果測試

---連接總數量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空閒的連接1url

咱們能夠看到鏈接的總數在不斷的增長,使用的鏈接也在不斷的增長,這說明有鏈接泄露,spa

緣由是直接經過數據源獲取鏈接(jdbcTemplate.getDataSource().getConnection())而沒有顯式釋放形成的。固然你能夠手動去顯示的釋放鏈接。這就多多少少的改成手工管理了,有點和spring事務管理機制相違背的味道。

那麼,應該怎麼樣從數據源中往外取鏈接呢?,經過DataSourceUtils 來獲取鏈接,看下面的代碼

 咱們先來看一下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
      首先嚐試從事務上下文中獲取鏈接,失敗後再從數據源獲取鏈接

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

可見此方法內部實現就是用doGetConnection(DataSource dataSource)方法來實現的,功能和上面的方法同樣。

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);
        }
    }

此方法就是經過上面的方法實現的,因此功能也同樣,釋放鏈接,歸還到鏈接池

咱們如今用DatasourceUtils來獲取鏈接

咱們只須要將代碼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

說明已經沒有鏈接泄露了,咱們經過DataSourceUtils的
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException

這個方法來獲取鏈接,並無顯示的釋放鏈接,鏈接並無泄露。

那麼,咱們使用DataSourceUtils工具類來獲取鏈接是否就很是的安全了呢?答案是否認的。

由於經過DataSourceUtils工具類在沒有事務上下文的方法中獲取鏈接使用,也會形成鏈接泄露。

咱們從配置文件xml中刪除事務的配置。原本不想貼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>
<!--這裏是事務配置,-->
<!--
    <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

數據鏈接有開始泄露來了!那麼咱們應該怎麼作呢。要想避免鏈接泄露,咱們須手動去關閉鏈接了,經過DataSourceUtils工具類

改造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

咱們看到鏈接數量好像不對稱了,最後一行,使用的2,空閒的2,而鏈接總數爲3,具體什麼緣由我沒有去深究,

個人猜測是鏈接回收時有延遲, 而回收鏈接結束正好是再4處完成,因此空閒數量就增長了1,因此 形成了鏈接的不對稱,若是有高手知道,請留言,不勝感激啊

System.out.println("---連接總數量" + cpds.getNumConnections()

                        + "使用中的連接" + cpds.getNumBusyConnections() +

"4"+
"空閒的連接"

                        + cpds.getNumIdleConnectionsAllUsers());

不一樣數據訪問框架 DataSourceUtils 的等價類
數據訪問技術框架 鏈接 ( 或衍生品 ) 獲取工具類
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
相關文章
相關標籤/搜索