JDBC学习笔记——简单的连接池

1、使用LinkedList保存连接                                                               

      即使是最简单的JDBC操作,也需要包含以下几步:建立连接、创建SQL语句、执行语句、处理执行结果、释放资源,其中建立连接步骤是很耗费计算机性能的,如果我们每次进行JDBC操作都创建新的JDBC连接,使用完后再立即释放连接,这样做会耗费大量性能。更合理的做法应该是:创建JDBC连接,使用JDBC连接,使用完后不是立刻释放JDBC连接,而是把连接缓存起来,当下一次操作JDBC时,我们可以直接使用缓存中已经连接上的JDBC连接。

      以下的代码通过一个LinkedList保存创建的JDBC连接,在刚创建的时候,我们会先建立10个连接并保存在list中,当客户代码需要使用Connection的时候,我们直接从list中取出第一个Connection返回,当客户代码使用完Connection,需要调用free()方法,把Connection再放回list中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public  class DataSource1 {
     LinkedList< Connection > connectionPool = new LinkedList< Connection >();
 
     public  DataSource1() {
         try {
             for  ( int  i = 0; i < 10; i++) {
                 this.connectionPool.addLast(this.createConnection());
             }
         } catch (SQLException e) {
             throw new ExceptionInInitializerError(e);
         }
     }
     
     public  Connection  getConnection() {
         return  connectionPool.removeFirst();
     }
     
     public  void free ( Connection  conn) {
         connectionPool.addLast(conn);
     }
     
     private Connection  createConnection() throws SQLException {
         return  DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc" ,
                 "root" , "" );
     }
}

2、控制连接数量                                                                             

      在上例的getConnection()方法中,我们没有检测connectionPool中是否包含Connection,直接返回connectionPool中的第一个Connection,这样做是不安全的,在从connectionPool中返回Connection之前,我们首先应该检测connectionPool中是否有Connection,若有,返回connectionPool中的第一个Connection,如果没有,我们可以新创建一个并返回。但是这样有一个问题,如果请求的线程很多,我们这样无限制地创建很多Connection可能导致数据库阻塞,因为数据库可以支持的连接数是有限的,所以我们应该控制新建Connection的上限,若没有达到上限,我们可以创建并返回,如果达到连接上限,那么我们就抛出异常。同时我们应该在getConnection()上加锁,保证多线程安全性,修改后代码如下,我们用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示现在存活的Connection数量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public  class  DataSource2 {
     private  static  int  initCount = 10 ;
     private  static  int  maxCount = 30 ;
     private  int  currentCount = 0 ;
 
     LinkedList<Connection> connectionPool = new  LinkedList<Connection>();
 
     public  DataSource2() {
         try  {
             for  ( int  i = 0 ; i < initCount; i++) {
                 this .connectionPool.addLast( this .createConnection());
                 this .currentCount++;
             }
         } catch  (SQLException e) {
             throw  new  ExceptionInInitializerError(e);
         }
     }
 
     public  Connection getConnection() throws  SQLException {
         synchronized  (connectionPool) {
             if  (connectionPool.size() > 0 )
                 return  connectionPool.removeFirst();
 
             if  ( this .currentCount < maxCount) {
                 this .currentCount++;
                 return  createConnection();
             }
 
             throw  new  SQLException( "已没有链接" );
         }
     }
 
     public  void  free(Connection conn) {
         connectionPool.addLast(conn);
     }
 
     private  Connection createConnection() throws  SQLException {
         return  DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc" ,
                 "root" , "" );
     }
}

3、动态代理拦截close方法                                                          

      上面的代码有个问题,那就是要关闭Connection必须调用我们的free()方法,不能直接调用Connection上的close()方法,这对于一些习惯使用完Connection就close()的用户并不是很友好,很容易导致它们忘记把用完的Connection还回connectionPool,为了让用户保持原有习惯,我们希望能够改写Connection的close()方法,让它不是直接关闭连接,而是把连接还回connectionPool中,其他方法保持原来不变,对于这种需求,我们可以使用动态代理实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public  class  DataSource3 {
     private  static  int  initCount = 1 ;
     private  static  int  maxCount = 1 ;
     int  currentCount = 0 ;
 
     LinkedList<Connection> connectionsPool = new  LinkedList<Connection>();
 
     public  DataSource3() {
         try  {
             for  ( int  i = 0 ; i < initCount; i++) {
                 this .connectionsPool.addLast( this .createConnection());
                 this .currentCount++;
             }
         } catch  (SQLException e) {
             throw  new  ExceptionInInitializerError(e);
         }
     }
 
     public  Connection getConnection() throws  SQLException {
         synchronized  (connectionsPool) {
             if  ( this .connectionsPool.size() > 0 )
                 return  this .connectionsPool.removeFirst();
 
             if  ( this .currentCount < maxCount) {
                 this .currentCount++;
                 return  this .createConnection();
             }
 
             throw  new  SQLException( "已没有链接" );
         }
     }
 
     public  void  free(Connection conn) {
         this .connectionsPool.addLast(conn);
     }
 
     private  Connection createConnection() throws  SQLException {
         Connection realConn = DriverManager.getConnection(
                 "jdbc:mysql://localhost:3306/jdbc" , "root" , "" );
         MyConnectionHandler proxy = new  MyConnectionHandler( this );
         return  proxy.bind(realConn);
     }
}
 
 
class  MyConnectionHandler implements  InvocationHandler {
     private  Connection realConnection;
     private  Connection warpedConnection;
     private  DataSource3 dataSource;
 
     MyConnectionHandler(DataSource3 dataSource) {
         this .dataSource = dataSource;
     }
 
     Connection bind(Connection realConn) {
         this .realConnection = realConn;
         this .warpedConnection = (Connection) Proxy.newProxyInstance( this
                 .getClass().getClassLoader(), new  Class[] { Connection. class  },
                 this );
         return  warpedConnection;
     }
 
     @Override
     public  Object invoke(Object proxy, Method method, Object[] args)
             throws  Throwable {
         if  ( "close" .equals(method.getName())) {
             this .dataSource.connectionsPool.addLast( this .warpedConnection);
         }
         return  method.invoke( this .realConnection, args);
     }
}

  上述代码的关键点是MyConnectionHandler类,在该类的bind()方法中返回了一个wrapedConnection,wrapedConnection的创建方法如下:

1
this .warpedConnection = (Connection) Proxy.newProxyInstance( this .getClass().getClassLoader(), <br>         new  Class[] { Connection. class  }, this );

  Proxy.newProxyInstance()方法是Java动态代理的关键方法,该方法会在运行时在内存中动态创建一个类,该方法的第一个参数是指定一个ClassLoader,第二个参数指定动态创建类实现的接口,第三个参数指定在该类上调用的方法应该转给哪个类,在这里指定了this,所以所有方法都会转给MyConnectionHandler,准确的说是MyConnectionHandler的invoke方法:

1
2
3
4
5
6
7
public  Object invoke(Object proxy, Method method, Object[] args)
         throws  Throwable {
     if  ( "close" .equals(method.getName())) {
         this .dataSource.connectionsPool.addLast( this .warpedConnection);
     }
     return  method.invoke( this .realConnection, args);
}

  在invoke()方法中,我们可以看到第二个参数传递了调用的Method,我们根据传递的Method对象判断,用户调用的是否是close方法,如果是close方法,那么我们就把这个Connection重新加入list,如果不是,那么我们就执行真正Connection上的相应方法。

      还需要注意的是,现在调用createConnection()产生的Connection对象已经不是原始的Connection对象,而是调用MyConnectionHandler类上bind()方法动态产生的代理类:

1
2
3
4
5
6
private  Connection createConnection() throws  SQLException {
     Connection realConn = DriverManager.getConnection(
             "jdbc:mysql://localhost:3306/jdbc" , "root" , "" );
     MyConnectionHandler proxy = new  MyConnectionHandler( this );
     return  proxy.bind(realConn);
}
本项目是一个基于SSM(Spring+SpringMVC+MyBatis)框架和Vue.js前端技术的大学生第二课堂系统,旨在为大学生提供一个便捷、高效的学习和实践平台。项目包含了完整的数据库设计、后端Java代码实现以及前端Vue.js页面展示,适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 在功能方面,系统主要实现了以下几个模块:用户管理、课程管理、活动管理、成绩管理和通知公告。用户管理模块支持学生和教师的注册、登录及权限管理;课程管理模块允许教师上传课程资料、设置课程时间,并由学生进行选课;活动管理模块提供了活动发布、报名和签到功能,鼓励学生参与课外实践活动;成绩管理模块则用于记录和查询学生的课程成绩和活动参与情况;通知公告模块则实时发布学校或班级的最新通知和公告。 技术实现上,后端采用SSM框架进行开发,Spring负责业务逻辑层,SpringMVC处理Web请求,MyBatis进行数据库操作,确保了系统的稳定性和扩展性。前端则使用Vue.js框架,结合Axios进行数据请求,实现了前后端分离,提升了用户体验和开发效率。 该项目不仅提供了完整的源代码和相关文档,还包括了详细的数据库设计文档和项目部署指南,为学习和实践提供了便利。对于基础较好的学习者,可以根据自己的需求在此基础上进行功能扩展和优化,进一步提升自己的技术水平和项目实战能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值