原理:
提前创建好一定数量的数据库链接,然后将其放入链接池里面,无论这些链接后续是否有被使用到,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
实现部分:
首先是将自己的propertise文件里面的信息导入到一个专门用来存储静态常量的类当中:
package org.lh.ibernate.config; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import static org.lh.ibernate.common.IbernateConstant.PROPERTIES_LOCATION; public class DbConfig { public static String DRIVER; public static String URL; public static String USERNAME; public static String PASSWORD; //数据库连接池初始化 public static int INITCOUNT; //连接池不足的时候增长的步进值 public static int STEP; //数据库连接池的最大链接量 public static int MAXCOUNT; //dao存放的位置 public static String DAO_PLACE; static /* 在静态块里面的内容会在程序找到函数的主入口之前提前读取,因此数据库的密码和用户名以及驱动之类的东西会提前预备好 */ { Properties proper=new Properties(); InputStream in =DbConfig.class.getResourceAsStream(PROPERTIES_LOCATION); try { proper.load(in); DRIVER=proper.getProperty("driver"); URL=proper.getProperty("url"); USERNAME=proper.getProperty("username"); PASSWORD=proper.getProperty("password"); INITCOUNT=Integer.valueOf(proper.getProperty("initcount")); STEP=Integer.valueOf(proper.getProperty("step")); MAXCOUNT=Integer.valueOf(proper.getProperty("maxcount")); DAO_PLACE=proper.getProperty("dao-package"); } catch (IOException e) { e.printStackTrace(); }finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } 复制代码
是的,这个类的设计主要是用于存储一些静态常量,并没有其他的功能。
关于数据链接是采用了java.sql.Connection 这个类。
但是每个如果只是这样引用的话是不够的,还需要添加一个标识符用于表示这个链接是否是处于繁忙状态中。所以我单独构造了一个对象出来:
package org.lh.ibernate.config; import java.sql.Connection; import java.sql.SQLException; //链接对象 public class DbPoolConn { public Connection connection; public boolean isBusy=false; public Connection getConnection() { return connection; } public void setConnection(Connection connection) { this.connection = connection; } public boolean isBusy() { return isBusy; } public void setBusy(boolean busy) { isBusy = busy; } //创建一个连接池连接(将其存储在连接池里面) public DbPoolConn(Connection connection,boolean isBusy){ this.connection=connection; this.isBusy=isBusy; } //相当于释放掉当前的连接对象 public void freeConnection() throws SQLException { this.isBusy=false; } } 复制代码
给每个链接对象都表示好状态符之后就可以进行更加系统的设计了,现在我们来编写连接池的代码
package org.lh.ibernate.config; import org.lh.ibernate.core.IDbPool; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Vector; //数据库连接池 public class DbPool implements IDbPool { private Vector<DbPoolConn> dbPoolConnVector=new Vector<>(); private static String DRIVER; private static String URL; private static String USERNAME; private static String PASSWORD; private static int INITCOUNT; private static int STEP; private static int MAXCOUNT; /* * * @author idea * @date 2018/7/11 * @param [count] * @return void * @des 创建连接池的核心 */ @Override public void createDbPoolConn(int count) throws Exception { System.out.println("this is createDbpoolConn!!"+dbPoolConnVector.size()); if(count>MAXCOUNT||(dbPoolConnVector.size())>MAXCOUNT){ throw new Exception("数据库初始化出错!链接数太大"); }else { for(int i=0;i<count;i++){ Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); DbPoolConn dbPoolConn=new DbPoolConn(conn,false); dbPoolConnVector.add(dbPoolConn); //加入向量队列中 } } } public DbPool() throws Exception { init(); try { Class.forName(DRIVER); //加载驱动 } catch (Exception ex) { ex.printStackTrace(); } createDbPoolConn(INITCOUNT); } private void init(){ this.DRIVER=DbConfig.DRIVER; this.URL=DbConfig.URL; this.USERNAME=DbConfig.USERNAME; this.PASSWORD=DbConfig.PASSWORD; this.INITCOUNT=DbConfig.INITCOUNT; this.STEP=DbConfig.STEP; this.MAXCOUNT=DbConfig.MAXCOUNT; } //如果链接数量大于初始化数值,创建新的链接 @Override public DbPoolConn getDbPoolConn() throws Exception { if(dbPoolConnVector.size()<INITCOUNT){ throw new Exception("链接初始化时出错!"); } System.out.println(dbPoolConnVector.size()); if(!hasFreeConn()){ //如果链接超多了初始连接数量,而且原有的连接池全部都满了 createDbPoolConn(STEP); DbPoolConn dbPoolConn=this.getRealConnectionFromPool(); return dbPoolConn; }else if(dbPoolConnVector.size()<MAXCOUNT){ DbPoolConn dbPoolConn=this.getRealConnectionFromPool(); return dbPoolConn; }else{ throw new Exception("对不起,连接池已满"); } } private boolean hasFreeConn(){ boolean hasFreeConn=false; int i=0; for (DbPoolConn dbPoolConn : dbPoolConnVector) { System.out.println(i+"----"+dbPoolConn.isBusy); if(dbPoolConn.isBusy==false){ hasFreeConn=true; } i++; } return hasFreeConn; } //如果链接已被占用,则创建新的链接,否则继续用原来的连接 private synchronized DbPoolConn getRealConnectionFromPool() throws SQLException { if(dbPoolConnVector.size()>0){ for (DbPoolConn dbPoolConn:dbPoolConnVector){ if(!dbPoolConn.isBusy){ //判断该链接是否有被释放掉(3秒内可以被连接上) if(dbPoolConn.connection.isValid(3000)){ dbPoolConn.setBusy(true); return dbPoolConn; }else{ Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); dbPoolConn.setBusy(true); dbPoolConn.setConnection(conn); dbPoolConnVector.add(dbPoolConn); return dbPoolConn; } } } } return null; } } 复制代码
连接池里面要注意,如果获取的连接超时的话,要进行相应的处理。
最后在编写一个相应的连接池工厂,用于提供给外界开发者使用:
package org.lh.ibernate.config; import org.lh.ibernate.core.IDbPool; //单例模式 public class DbPoolFactory { public static IDbPool iDbPool; static { try { iDbPool = new DbPool(); } catch (Exception e) { e.printStackTrace(); } } public static IDbPool getInstance(){ return iDbPool; } } 复制代码
经过测试,本连接池性能为2000次链接,消耗时间为290ms。
import org.junit.Before; import org.junit.Test; import org.lh.ibernate.config.DbPoolConn; import org.lh.ibernate.config.DbPoolFactory; import org.lh.ibernate.core.IDbPool; import java.sql.Connection; public class DbTest { private DbPoolFactory dbPoolFactory = new DbPoolFactory(); private IDbPool iDbPool; @Before public void setUp() { iDbPool = DbPoolFactory.getInstance(); } @Test public void testConnection10() throws Exception { long begin = System.currentTimeMillis(); for (int i = 0; i < 2000; i++) { DbPoolConn dbpoolConn = iDbPool.getDbPoolConn(); Connection connection = dbpoolConn.getConnection(); connection = null; dbpoolConn.freeConnection(); } long end = System.currentTimeMillis(); System.out.println("消耗时间:" + (end - begin)); } } 复制代码
在资源有限的情况下,每一次获取到相应的连接之后,用完都需要通过
dbpoolConn.freeConnection();这个函数来进行资源的释放,放回到原先的数据连接池当中,这样才能够提高资源的利用率。
一般对于数据库访问量小的项目中,数据库连接池的意义并不大,在数据库访问量规模较大的项目中,连接池真正的性能优势才会体现出来。