数据库链接池
直接获取链接的缺点:
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
什么是数据库链接池
数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。这样,以后我们在获取数据库链接时,就直接从连接池中取,而不是向数据库要,这样极大了提高了链接的利用率,缓解了服务器的压力。
最小链接数与最大连接数
数据库链接池在初始化时将创建一定数量的数据库连接放到链接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的链接数量
链接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
- 数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
创建自己的数据库链接池
1) 实现 java.sql.DataSource接口
2) 申请一批链接(并将这些链接,加入到我们自己维护的链表中)
3) 复写getConnection()方法, 即获取链接时, 要从我们自己维护的链表中获取
4) 增强connection对象的close()方法。 原因:在关闭链接时,我们应使链接回到池中
代码范例:
使用动态代理技术来增强Connection的close()方法。
public class JdbcPool implements DataSource {
private static LinkedList<Connection> list = new LinkedList<Connection>();
private static Properties config = new Properties();
private Connection conn = null;
static{
try {
config.load(JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("db.properties")); //通过读取配置文件的方式来获得数据库的配置信息
Class.forName(config.getProperty("driver"));
for(int i=0;i<10;i++){
Connection conn = DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password"));
list.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public Connection getConnection() throws SQLException {
if(list.size()<=0){
throw new RuntimeException("数据库忙,请稍会再来!!");
}
conn = list.getFirst();
return (Connection) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("close")) {
list.addLast(conn);
return null;
}
return method.invoke(conn, args);
}
});
}
}
一些开源的数据库链接池
许多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现,例如:
DBCP 数据库连接池
C3P0 数据库连接池
在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。
DBCP数据源
tomcat内置的连接池就是DBCP。 (都是apache的产品)
使用步骤:
1. 导入相关jar包: common-dbcp.jar(连接池的实现), common-pool.jar(接池实现的依赖库)
2. 在类目录下加入dbcp的配置文件:dbcpconfig.properties
文件内容:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcCollection
username=root
password=root
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
- 编写获取链接的DBCP工具类
public class JdbcUtils_DBCP {
private static DataSource ds = null;
/*在静态代码块中创建链接池*/
static{
try{
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = new BasicDataSourceFactory();
ds = factory.createDataSource(prop);//创建DBCP数据源, 需要读取配置文件
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
/*释放资源:Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象*/
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close(); //throw new
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
c3p0数据源
C3P0数据源的使用,要比DBCP简单, 并且spring内置的数据源就是c3p0。
c3p0与dbcp区别:
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
使用步骤:
1. 导入相关jar包: c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.2-pre1.jar
2. 在类目录下加入C3P0的配置文件:c3p0-config.xml
内容如下:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day16</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</default-config>
<named-config name="mysql">
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<!-- intergalactoApp adopts a different approach to configuring statement caching -->
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
<named-config name="oracle">
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property><!-- intergalactoApp adopts a different approach to
configuring statement caching -->
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
- 编写获取链接的c3p0工具类
public class JdbcUtils_C3P0 {
private static ComboPooledDataSource ds = null;
static{
try{
//这里可以不写配置文件的名称。 默认会读取类目录下的c3p0-config.xml, 并装载默认配置
ds = new ComboPooledDataSource();
}catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
public static void release(Connection conn,Statement st,ResultSet rs){
if(rs!=null){
try{
rs.close(); //throw new
}catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if(st!=null){
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
st = null;
}
if(conn!=null){
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
使用Tomcat内置数据源
在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么我们在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候帮我们创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。
JNDI
JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这 套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需 通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
- 配置数据源
Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源,
配置方式:
<Context>
<Resource name="jdbc/datasource" auth="Container"
type="javax.sql.DataSource" username="root" password="XDP"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/jdbcstudy"
maxActive="8" maxIdle="4"/>
</Context>
NT:该配置可以在web应用的META-INF目录下新建一个context.xml, 在这个文件中配置, 其实这就相当于在tomacat的conf/Catalina/localhost/目录下配置
Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,
JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的
资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建>很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提
供给我们的应用程序使用:第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的>request对象和response对象就是服务器
以参数的形式传递给我们的。第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。即从服务器获取资源的两种形式:
1. 通过方法的参数(request、 response)
2. 通过JNDI容器获取
- 从JNDI容器中获取数据源
可通过如下代码获取:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此种配置下,数据库的驱动jar文件需放置在tomcat的lib下, 因为Tomcat要去找,并且创建
NT:本博文参考自 [(http://www.cnblogs.com/xdp-gacl/p/4002804.html)]