一、什么是JDBC连接池?
在传统的JDBC连接中,每次获得一个Connection连接都需要加载通过一些繁杂的代码去获取,例如以下代码:
public static Connection getConn(){ Connection conn = null; String url = "jdbc:mysql://localhost:3306/test"; String user = "root"; String password = "root"; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; }
这样繁杂的操作只为了获取一次连接,当然,我们可以将其封装成一个工具类来访问(上图以封装好Connection的连接),但是每一次连接都需要取加载一次是不是很浪费性能,为了优化性能,那么就出现了连接池。
连接池在初始化的时候就创建了几个连接供我们使用,当我们需要连接时只需要从连接池中获取已存在的连接,当初始化的几个连接都没有时,会重新创建一个连接,使用完连接后不会去销毁连接,而是归还给连接池供后面需要连接的使用。(当然,连接池不仅仅只是这么简单,这里就只做这些介绍)
常用的连接池有DBCP、C3P0,现在最主流是好像是阿里的Druid连接池,还有tomcat的自带的JNDI连接池
二、自定义一个简单的连接池
对自定义连接池的分析:
1.2.因为是连接池 ,我们需要实现DataSource接口,并实现其中的方法,基于我们的情况,我们关于与getConnection()方法;
2.既然要存放几个连接对象,那么我们用一个集合来存放它,基于会经常操作增加和删除那么选用LinkedList;
3.连接的销毁并不是销毁连接,而是将连接归还给连接池
编码:
1.创建一个类MyDataSource 并实现DataSource接口
当此类加载时它就需要有一个容器来存放Connection,所以定义一个静态的属性:
private static ListconnectionList = new LinkedList<>();
2.因为需要取获得数据库连接,所以我们封装一个获取数据库连接的方法
public Connection getOneConnection(){ Connection conn = null; try{ //此处通过外部的properties文件来获取的数据,这样更加灵活。 InputStream in = MyDataSource.class.getClassLoader(). getResourceAsStream("jdbc/jdbc.properties"); Properties pro = new Properties(); pro.load(in); driver = pro.getProperty("driver"); url = pro.getProperty("url"); username = pro.getProperty("user"); password = pro.getProperty("password"); Class.forName(driver); conn = DriverManager.getConnection(url,username,password); }catch (Exception e){ e.getStackTrace(); } return conn; }
注意的是我这里通过propertie文件的获取的数据,可根据实际情况来选择
3.初始化几个连接放入容器中。可以使用静态代码块来实现,但是如果没有使用此数据源那么就造成了资源的浪费,所以我考虑将初始化几个连接的实现放到他的构造方法中,即当需要此连接池的时候他才会随之创建几个连接。如下:
public MyDataSource() { for (int i = 0; i < 5; i++) { Connection conn = getOneConnection();//调用创建连接的方法 connectionList.add(conn); } }
4.现在开始重写外部从此连接池中获取连接的方法getConnection()
@Override public Connection getConnection() throws SQLException { Connection conn = null; if(connectionList == null || connectionList.size() <= 0){ Connection connection = getConnection(); connectionList.add(connection); } conn = connectionList.remove(0); return conn; }
5.创建一个对象返回的方法,即将用完的连接放入归还到连接池中
public void backConnection(Connection conn){ connectionList.add(conn); }
OK,这样就完成了一个简单的自定义连接池,测试代码如下:
public static void main( String[] args ) throws SQLException { MyDataSource dataSource = new MyDataSource(); Connection conn = dataSource.getConnection(); String sql = "select * from user where u_id = ?"; PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(sql); ps.setInt(1, 1); rs = ps.executeQuery(); while (rs.next()) { System.out.println("id="+rs.getInt(1)); System.out.println("username="+rs.getString(2)); System.out.println("password="+rs.getString(3)); } } catch (SQLException e) { e.printStackTrace(); } finally { dataSource.backConnection(conn); } }
因为忽略,我的代码中没有关闭其他两个对象。
现在有一个小问题就是,我们的关闭连接是通过连接池的方法来实现的,但是,如果用户调用Connection对象的close方法,那么连接时被销毁了,并没有返回给连接池,那么我们来优化它,让用户使用close()方法不会去销毁连接,而是去归还连接。
方案有很多中,这里采用装饰着模式的一种。
优化:
1.新建一个类MyConnection来实现Connection接口,其中他有属性类型为Connection conn和一个Liis
private Connection conn; private Listpool; public MyConnection(Connection conn, List pool) { this.conn = conn; this.pool = pool; }
2.然后实现接口的close方法
@Override public void close() throws SQLException { System.out.println("回收连接"); pool.add(conn); }
3.然后实现他获取Statement的方法,如果不实现那么获取此Statement会出现空指针错误,我这里就只实现了PreparedStatement的获取方法
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { System.out.println("获得Statement"); return conn.prepareStatement(sql); }
4.然后删除掉MyDataSource类中归还连接的方法backConnection,并将构造方法和获取连接的方法做如下改造
public MyDataSource2() { for (int i = 0; i < 5; i++) { Connection conn = getOneConnection(); MyConnection myConn = new MyConnection(conn, connectionList); connectionList.add(myConn); } }
@Override public Connection getConnection() throws SQLException { Connection conn = null; if(connectionList == null || connectionList.size() <= 0){ Connection connection = getConnection(); MyConnection myConn = new MyConnection(connection, connectionList); connectionList.add(myConn); } conn = connectionList.remove(0); return conn; }
好了,这样用户直接调用我们的Connection的close方法就不会去销毁连接了,会正确的归还给了连接池了,对测试代码稍做修改即可测试。
忍者必须死34399账号登录版 最新版v1.0.138v2.0.72
下载勇者秘境oppo版 安卓版v1.0.5
下载忍者必须死3一加版 最新版v1.0.138v2.0.72
下载绝世仙王官方正版 最新安卓版v1.0.49
下载Goat Simulator 3手机版 安卓版v1.0.8.2
Goat Simulator 3手机版是一个非常有趣的模拟游
Goat Simulator 3国际服 安卓版v1.0.8.2
Goat Simulator 3国际版是一个非常有趣的山羊模
烟花燃放模拟器中文版 2025最新版v1.0
烟花燃放模拟器是款仿真的烟花绽放模拟器类型单机小游戏,全方位
我的世界动漫世界 手机版v友y整合
我的世界动漫世界模组整合包是一款加入了动漫元素的素材整合包,
我的世界贝爷生存整合包 最新版v隔壁老王
我的世界MITE贝爷生存整合包是一款根据原版MC制作的魔改整