有这样的一种现象:html
用java代码操做数据库,须要数据库链接对象,一个用户至少要用到一个链接。如今假设有成千上百万个用户,就要建立十分巨大数量的链接对象,这会使数据库承受极大的压力,为了解决这种现象,一种技术出现了,这就是数据库链接池。java
所谓数据库链接池,能够看做 :在用户和数据库之间建立一个”池”,这个池中有若干个链接对象,当用户想要链接数据库,就要先从链接池中获取链接对象,而后操做数据库。一旦链接池中的链接对象被拿光了,下一个想要操做数据库的用户必须等待,等待其余用户释放链接对象,把它放回链接池中,这时候等待的用户才能获取链接对象,从而操做数据库。mysql
链接对象初始的数量:initSize,一开始就建立若干个,当不够时再添加sql
链接对象最大数量:maxSize,添加到最大值则不会再添加数据库
下面咱们用代码下一个本身的链接池吧~tomcat
看下面代码和注释吧~服务器
public class MyPool { //设置注册属性 private String url = "jdbc:mysql://localhost:3306/vmaxtam"; private String user = "root"; private String password = "root"; private static String driverClass="com.mysql.jdbc.Driver"; //设置链接池属性 private int initSize = 5; private int maxSize = 8; //用LinkedList对象来保存connection对象 private LinkedList<Connection> connList = new LinkedList<Connection>(); //声明一个临时变量来计算链接对象的数量 private int currentsize = 0; //声明MyPool对象时自动注册驱动 static{ try { Class.forName(driverClass); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //获取链接的方法 private Connection getConnection() { Connection conn=null; try { conn = DriverManager.getConnection(url, user, password); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } //构造方法,初始化链接池,并往里面添加链接对象 public MyPool() { for(int i = 0; i < initSize; i++) { Connection connection = this.getConnection(); connList.add(connection); currentsize++; } } //获取链接池中的一个链接对象 public Connection getConnFromPool() { //当链接池还没空 if(connList.size()>0){ Connection connection = connList.getFirst(); connList.removeFirst(); return connection; }else if(connList.size()==0&¤tsize<8){ //链接池被拿空,且链接数没有达到上限,建立新的链接 currentsize++; connList.addLast(this.getConnection()); Connection connection = connList.getFirst(); connList.removeFirst(); return connection; } throw new RuntimeException("链接数达到上限,请等待"); } //把用完的链接放回链接池 public void releaseConnection(Connection connection) { connList.addLast(connection); } }
有了链接池后,咱们写一个测试来调用一下它吧~并发
@Test public void test1() { //得到链接池 MyPool mypool = new MyPool(); /*从链接池中尝试获取9个链接 for(int i = 0 ; i<9; i++){ Connection conn = mypool.getConnFromPool(); System.out.println(conn.toString()); }*/ //获取第五个链接后,释放一下,而后再获取 for(int i = 0 ; i<9; i++){ Connection conn = mypool.getConnFromPool(); if(i==5){ mypool.releaseConnection(conn); } System.out.println(conn.toString()); } }
上面这样就实现了本身的一个链接池,可是这个链接池依然存在着不少问题,一个较为明显的问题就是: 框架
若是一个用户获取了一个链接对象,而后调用了close()方法把它关闭了,没有放回池中,这样池中的这个对象就回不来了,形成最大链接上限为8个的链接池实际上只有7个链接在工做。ide
为了解决这个问题,咱们须要对close()方法进行改造,是用户调用close()方法时,其实是把链接放回链接池中,而不是关闭它。
下面就为解决这个问题来分析下~
方法一:使用静态代理,写一个myConnection()类来继承connection的实现类,而后重写它的close()方法.
方法二:使用动态代理,使用jdbc动态代理类:java.lang.reflect.Proxy类
动态代理类中有这样一个方法能够建立它的实例
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
参数解析:
ClassLoader:类加载器,只要在同一个JDK中的类便可
Class<?>[]:要代理的接口的集合
InvocationHandler:代理接口的方法处理器
根据须要,咱们要给MyPool中的Connection加一个动态代理,因此咱们用的前两个参数是:MyPool.Class.GetClassLoader 与 new Class<>{Connection}
最后还剩方法处理器,咱们要修改Connection中的close方法,因此咱们写出一个这样作的处理器便可,具体实现看下面代码与注释~
//获取链接的方法 private Connection getConnection() { try { //获取一个链接 final Connection conn=DriverManager.getConnection(url, user, password); //把链接交给动态代理类转换为代理的链接对象 Connection myconn = (Connection)Proxy.newProxyInstance( MyPool.class.getClassLoader(), new Class[] {Connection.class}, //编写一个方法处理器 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object value = null; //当遇到close方法,就会把对象放回链接池中,而不是关闭链接 if(method.getName().equals("close")) { MyPool.connList.addLast(conn); }else { //其它方法不变 value = method.invoke(conn, args); } return value; }} ); return myconn; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } }
以上就是利用动态代理的方式解决close的问题了~~可是,咱们本身写的链接池还有不少其余问题:
1)当前多个并发用户同时获取链接时,可能拿到同一个链接对象
2)当前用户链接数超过了最大链接数时,不能直接抛出异常,应该有机制,控制用户等待时间........
因此,这时候已经有人站出来,为咱们写好了一些功能相对完善的链接池,这些第三方的链接池获得了普遍的用途,下面咱们来介绍一下常见的链接池工具吧~
简介:DBCP链接池是开源组织Apache软件基金组织开发的链接池实现。
事实上,tomcat服务器默认就会使用这个链接池道具。
如何使用DBCP链接池呢,下面我来一一演示。
步骤一:导包,使用第三方的道具,必须导入相应的jar包。
须要导入两个jar包:commons-dbcp-1.4.jar包
commons-pool-1.5.6.jar包
步骤二:使用代码~看看下面代码的演示吧
public class DBCPTest { private String url = "jdbc:mysql://localhost:3306/vmaxtam"; private String user = "root"; private String password = "root"; private String classDriver = "com.mysql.jdbc.Driver"; @Test public void Test1() { //建立DBCP链接池对象 BasicDataSource ds = new BasicDataSource(); //设置链接参数来进行链接 ds.setUrl(url); ds.setUsername(user); ds.setPassword(password); ds.setDriverClassName(classDriver); //而后能够设置链接池的一些属性啦~ ds.setInitialSize(5); ds.setMaxActive(8); ds.setMaxWait(3000);//设置最大的等待时长,毫秒为单位 //从链接池中获取对象 for(int i = 0 ; i<8;i++) { Connection conn = null; try { conn = ds.getConnection(); System.out.println(conn.hashCode()); } catch (SQLException e) { e.printStackTrace(); } } } }
为了测试效果,咱们能够在循环中设置拿9个链接额,这样在拿第九个链接时就会出现等待,等待到结束都没有链接被释放回链接池,就会出现报错。
也能够把For循环改为下面那样,测试close方法:
//从链接池中获取对象 for(int i = 0 ; i<9;i++) { Connection conn = null; try { conn = ds.getConnection(); System.out.println(conn.hashCode()); if(i==5) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } }
上面的代码仍是有点地方能够获得优化,例如能够经过配置文件来配置链接的参数,还有数据库链接池的属性参数。
配置文件:
url=jdbc:mysql://localhost:3306/vmaxtam username=root password=root classDriver=com.mysql.jdbc.Driver initialSize=5 maxActive=8 maxWait=3000
用对象读取配置文件:
@Test public void Test2() { try { //建立配置对象 Properties properties = new Properties(); properties.load(DBCPTest.class.getResourceAsStream("/dbcp.properties")); //建立链接池对象,而且用链接池工厂来加载配置对象的信息 BasicDataSource ds = (BasicDataSource)BasicDataSourceFactory.createDataSource(properties); //从链接池中获取对象 for(int i = 0 ; i<8;i++) { Connection conn = null; conn = ds.getConnection(); System.out.println(conn.hashCode()); } }catch (Exception e2) { e2.printStackTrace(); } }
以上就是DBCP链接池的基本用法了~下面咱们来学习另外一个链接池~
简介: C3P0是一个开源组织的产品,开源框架的内部的链接池通常都使用C3P0来实现,例如:Hibernate
步骤一:导包,使用第三方的工具必须导入jar包
要导入的包:c3p0-0.9.1.2.jar 包
步骤二:看下面的代码显示怎么使用这个链接池吧~
@Test public void Test1() { try { //获取链接池对象 ComboPooledDataSource cp = new ComboPooledDataSource(); //设置链接参数 cp.setJdbcUrl(url); cp.setUser(user); cp.setPassword(password); cp.setDriverClass(classDriver); //设置链接池的参数 cp.setInitialPoolSize(5);//初始数量 cp.setMaxPoolSize(8);//最大数量 cp.setCheckoutTimeout(3000);//最大等待时间 for(int i = 0 ; i<8 ; i++) { Connection conn = cp.getConnection(); System.out.println(conn.hashCode()); } } catch (Exception e) { e.printStackTrace(); } }
能够看出,C3P0的用法和DBCP的用法很是的类似~这里不作累赘。
特别的是C3PO读取参数文件的方式,C3P0除了能像DBCP那样读取配置文件,它还提供了一种特殊的设置参数的方式,就是把参数数据写在一个名叫c3p0-config.xml的XML文件中,在建立C3P0对象时会自动在classpath去寻找该文件来读取~
也就是说:c3p0会到classpath下读取名字为c3p0-config.xml文件
这份XML文件有特殊的要求,下面咱们来写一下这份XML文件:
<c3p0-config> <!-- 默认配置 --> <default-config> <property name="jdbcUrl">jdbc:mysql://localhost:3306/vmaxtam</property> <property name="user">root</property> <property name="password">root</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">8</property> <property name="checkoutTimeout">3000</property> </default-config> <!-- mysql的链接配置 --> <named-config name="mysql"> <property name="jdbcUrl">jdbc:mysql://localhost:3306/vmaxtam</property> <property name="user">root</property> <property name="password">root</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">8</property> <property name="checkoutTimeout">3000</property> </named-config> <!-- 使用 oracal就会用这份配置--> <!-- 也能够写其余数据库的配置 --> </c3p0-config>
写完xml文件,如今咱们就读取它吧~
@Test public void Test2() { try { //获取链接池对象,写上参数就会去找xml文件找这个数据库的配置来读取,当无参时,就会使用默认设置。 ComboPooledDataSource cp = new ComboPooledDataSource("mysql"); for(int i = 0 ; i<9 ; i++) { Connection conn = cp.getConnection(); System.out.println(conn.hashCode()); if(i==5) { conn.close(); } } } catch (Exception e) { e.printStackTrace(); } }
使用这种读取方法,显得代码十分简便。