JavaWeb入门实战—数据库连接池(数据源)

一 介绍

上一篇文章中讲到了通过Connection去访问并操作数据库,每次连接数据库都是创建一个Connection对象,访问完毕再close掉连接,如下:

 

这种方式的缺点很明显:用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、严重的话甚至宕机。

那有没有什么可优化的办法呢?答案是肯定的,那就是 数据库连接池技术(数据源)。


二 使用数据库连接池优化程序性能

原理:应用程序在启动的时候就初始化一批Connection对象到一个池子中(集合),当应用程序需要访问数据库的时候就从这个池子中取Connection对象,操作数据库完成后再将这个Connection对象对象放回到池子中去。这样就可以避免重复创建Connection对象,如下:



三 实现自己的数据库连接池

实现自己的连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

  • Connection getConnection() 
  • Connection getConnection(String username, String password) 
实现DataSource接口,并实现连接池功能的步骤:
  1. 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
  2. 实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
  3. 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。


package com.ricky;

import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;

import javax.sql.DataSource;

import com.itheima.exception.NotSupportException;

public class MyDataSource implements DataSource{
	private static LinkedList<Connection> pool = new LinkedList<Connection>();
	static{
		try{
			InputStream in = SimpleConnectionPool.class.getClassLoader().getResourceAsStream("dbcfg.properties");
			Properties props = new Properties();
			props.load(in);
			Class.forName(props.getProperty("className"));
			for(int i=0;i<10;i++){
				Connection conn = DriverManager.getConnection(props.getProperty("url"), props.getProperty("username"), props.getProperty("password"));
				pool.add(conn);
			}
			for(Connection conn:pool){
				System.out.println("初始化的连接为:"+conn);
			}
		}catch(Exception e){
			throw new ExceptionInInitializerError(e);
		}
	}
	public LinkedList<Connection> getPool(){
		return pool;
	}
	@Override
	public synchronized Connection getConnection() throws SQLException {
		//conn.commit() conn.pro  conn.close()
		if(pool.size()>0){
			final Connection conn = pool.removeFirst();
			//得到代理对象的实例
			Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					if(!method.getName().equals("close")){
						//调用原有对象的方法
						return method.invoke(conn, args);
					}else{
						//close
						return pool.add(conn);
					}
				}
			});
			System.out.println(proxyConn.getClass().getName());
			return proxyConn;
		}else{
			throw new RuntimeException("The Server is busy");
		}
	}

	@Override
	public Connection getConnection(String username, String password)
			throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public int getLoginTimeout() throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		throw new NotSupportException("This method not supported by this datasource");
	}

}


四 开源数据库连接池

        现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:

  • DBCP 数据库连接池 
  • C3P0 数据库连接池
        实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。

1、DBCP 

DBCP 是 Apache 软件基金组织下的开源连接池实现,Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

官网地址:http://commons.apache.org/proper/commons-dbcp/

使用方法

1.1、dbcp.properties

#连接数据库所用的 JDBC Driver Class,
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day14
username=root
password=sorry

#<!-- 连接池启动时创建的初始化连接数量(默认值为0) -->
initialSize=10

#连接池中可同时连接的最大的连接数,为0则表示没有限制,默认为8
maxActive=20

#<!-- 连接池中最大的空闲的连接数(默认为8,设 0 为没有限制),超过的空闲连接将被释放 -->
maxIdle=15

#<!-- 连接池中最小的空闲的连接数(默认为0,一般可调整5) -->
minIdle=5

#<!-- 超过时间会丟出错误信息 最大等待时间(单位为 ms) -->
maxWait=60000

connectionProperties=useUnicode=true;characterEncoding=UTF8

#对于事务是否 autoCommit, 默认值为 true
defaultAutoCommit=true

#对于数据库是否只能读取, 默认值为 false
defaultReadOnly=

#默认事物隔离几本,取值:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=REPEATABLE_READ

基础参数说明 
defaultAutoCommit: 对于事务是否 autoCommit, 默认值为 true
defaultReadOnly:对于数据库是否只能读取, 默认值为 false
initialSize:连接池启动时创建的初始化连接数量(默认值为0)
driverClassName:连接数据库所用的 JDBC Driver Class,
url: 连接数据库的 URL
username:登陆数据库所用的帐号
password: 登陆数据库所用的密码
maxActive: 连接池中可同时连接的最大的连接数,为0则表示没有限制,默认为8
maxIdle: 连接池中最大的空闲的连接数(默认为8,设 0 为没有限制),超过的空闲连接将被释放,如果设置为负数表示不限制(maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建)
minIdle:连接池中最小的空闲的连接数(默认为0,一般可调整5),低于这个数量会被创建新的连接(该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接)
maxWait: 超过时间会丟出错误信息 最大等待时间(单位为 ms),当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为-1,一般可调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
validationQuery: 验证连接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned:超过removeAbandonedTimeout时间后,是否进行没用连接的回收(默认为false)
removeAbandonedTimeout: 超过时间限制,回收五用的连接(默认为 300秒),removeAbandoned 必须为 true
logAbandoned: 是否记录中断事件, 默认为 false

1.2 示例代码

package com.itheima.pool;

import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPDemo1 {

	public static void main(String[] args) throws Exception {
		Properties props = new Properties();
		InputStream in = DBCPDemo1.class.getClassLoader().getResourceAsStream("dbcp.properties");
		props.load(in);
		BasicDataSourceFactory facotry = new BasicDataSourceFactory();
		DataSource ds = facotry.createDataSource(props);
		Connection conn = ds.getConnection();
		System.out.println(conn.getClass().getName());
		conn.close();//换回池中
	}
}


2、C3P0

C3P0是一个更加强大、配置更灵活的数据源,官网地址:http://www.mchange.com/projects/c3p0/

2.1 通过代码设置各个配置项

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(props.getProperty("driverClass"));
cpds.setJdbcUrl(props.getProperty("jdbcUrl"));
cpds.setUser(props.getProperty("user"));
cpds.setPassword(props.getProperty("password"));


2.2 通过配置文件

类路径下提供一个c3p0-config.xml文件,如下:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///day14</property>
		<property name="user">root</property>
		<property name="password">sorry</property>
		<property name="initialPoolSize">15</property>
		<property name="maxIdleTime">30</property>
		<property name="maxPoolSize">20</property>
		<property name="minPoolSize">5</property>
		<property name="maxStatements">2000</property>
	</default-config> <!-- This app is massive! -->
	<named-config name="day14">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///day14</property>
		<property name="user">root</property>
		<property name="password">sorry</property>
		<property name="initialPoolSize">15</property>
		<property name="maxIdleTime">30</property>
		<property name="maxPoolSize">20</property>
		<property name="minPoolSize">5</property>
		<property name="maxStatements">2000</property>
	</named-config>
</c3p0-config>

获取数据源方法如下:

//ComboPooledDataSource ds = new ComboPooledDataSource();//获取配置文件中的default-config
ComboPooledDataSource ds = new ComboPooledDataSource("day14");
Connection conn = ds.getConnection();

更详细的用法可参考这篇文章:http://my.oschina.net/lyzg/blog/55133



JDBC数据源的讲解到这里就结束了,后续文章会介绍一下数据库操作框架,例如DBUtils、MyBatis等等。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值