看下面的代码,其中链接池采用的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
public void addPerson(){
try {sql
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++) {工具
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.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 |