Spring的事务管理难点剖析(7):数据连接泄漏(二)

2014-11-24 08:51:31 · 作者: · 浏览: 8
ctive()+":"+basicDataSource.getNumIdle()+"]");

36 }

37

38 public static void main(String[] args) {

39 ApplicationContext ctx =

40 new ClassPathXmlApplicationContext("com/baobaotao/connleak/applicatonContext.xml");

41 JdbcUserService userService = (JdbcUserService) ctx.getBean("jdbcUserService");

42

43 BasicDataSource basicDataSource = (BasicDataSource) ctx.getBean("dataSource");

44

45 //④汇报数据源初始连接占用情况

46 JdbcUserService.reportConn(basicDataSource);

47

48 JdbcUserService.asynchrLogon(userService, "tom");//启动一个异常线程A

49 JdbcUserService.sleep(500);

50

51 //⑤此时线程A正在执行JdbcUserService#logon()方法

52 JdbcUserService.reportConn(basicDataSource);

53

54 JdbcUserService.sleep(2000);

55

56 //⑥此时线程A所执行的JdbcUserService#logon()方法已经执行完毕

57 JdbcUserService.reportConn(basicDataSource);

58

59 JdbcUserService.asynchrLogon(userService, "john");//启动一个异常线程B

60 JdbcUserService.sleep(500);

61

62 //⑦此时线程B正在执行JdbcUserService#logon()方法

63 JdbcUserService.reportConn(basicDataSource);

64

65 JdbcUserService.sleep(2000);

66

67 //⑧此时线程A和B都已完成JdbcUserService#logon()方法的执行

68 JdbcUserService.reportConn(basicDataSource);

69

70 }

在JdbcUserService中添加一个可异步执行logon()方法的asynchrLogon()方法,我们通过异步执行logon()以及让主 线程睡眠的方式模拟多线程环境下的执行场景。在不同的执行点,通过reportConn()方法汇报数据源连接的占用情况。
通过Spring事务声明,对JdbcUserServie的logon()方法进行事务增强,配置代码如下所示:

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

02 http://www.springframework.org/schema/beans"

03 …

04 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

05

06

07

08 destroy-method="close"

09 p:driverClassName="${jdbc.driverClassName}"

10 p:url="${jdbc.url}"

11 p:username="${jdbc.username}"

12 p:password="${jdbc.password}"/>

13

14

15 class="org.springframework.jdbc.core.JdbcTemplate"

16 p:dataSource-ref="dataSource"/>

17

18

19 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

20 p:dataSource-ref="dataSource"/>

21

22

23

24

然后,运行JdbcUserServie,在控制台将观察到如下的输出信息:

引用
连接数[active:idle]-[0:0]
连接数[active:idle]-[2:0]
连接数[active:idle]-[1:1]
连接数[active:idle]-[3:0]
连接数[active:idle]-[2:1]

我们通过表10-3对数据源连接的占用和泄漏情况进行描述。
时间 执行线程1 执行线程2 数据源连接

active idle leak
T0 未启动 未启动 0 0 0
T1 正在执行方法 未启动 2 0 0
T2 执行完毕 未启动 1 1 1
T3 执行完毕 正式执行方法 3 0 1
T4 执行完毕 执行完毕 2 1 2

可见在执行线程1执行完毕后,只释放了一个数据连接,还有一个数据连接处于active状态,说明泄漏了一个连接。相似的,执行线程2执行完毕后,也泄漏 了一个连接:原因是直接通过数据源获取连接(jdbcTemplate.getDataSource().getConnection())而没有显式释 放。

通过DataSourceUtils获取数据连接

Spring提供了一个能从当前事务上下文中获取绑定的数据连接的工具类,那就是DataSourceUtils。Spring强调必须使用 DataSourceUtils工具类获取数据连接,Spring的JdbcTemplate内部也是通过DataSourceUtils来获取连接 的。 DataSourceUtils提供了若干获取和释放数据连接的静态方法,说明如下:


static Connection doGetConnection(DataSource dataSource):首先尝试从事务上下文中获取连接,失败后再从数据源获取连接;
static Connection getConnection(DataSource dataSource):和doGetConnection方法的功能一样,实际上,它内部就是调用doGetConnection方法获取连接的;
static voi