常用数据库连接池

Posted on 2013-07-26 18:46  冰天雪域  阅读(657)  评论(0编辑  收藏  举报

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

 

 

 

 

 

 

 

2、数据库连接池编写原理分析

1编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

Connection getConnection() 

Connection getConnection(String username, String password) 

2实现DataSource接口,并实现连接池功能的步骤:

DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中

实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。

当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。

package com.itheima.pool;

 

import java.io.PrintWriter;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.sql.Array;

import java.sql.Blob;

import java.sql.CallableStatement;

import java.sql.Clob;

import java.sql.Connection;

import java.sql.DatabaseMetaData;

import java.sql.DriverManager;

import java.sql.NClob;

import java.sql.PreparedStatement;

import java.sql.SQLClientInfoException;

import java.sql.SQLException;

import java.sql.SQLWarning;

import java.sql.SQLXML;

import java.sql.Savepoint;

import java.sql.Statement;

import java.sql.Struct;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.Properties;

 

import javax.sql.DataSource;

public class MyPool implements DataSource {

private static List<Connection> poolList = new LinkedList<Connection>();

static{

try{

Class.forName("com.mysql.jdbc.Driver");

for(int i = 0;i<5; i++){

Connection conn = DriverManager.getConnection("jdbc:mysql:///day11","root","root");

poolList.add(conn);

}

}catch (Exception e) {

e.printStackTrace();

throw new RuntimeException();

}

}

public Connection getConnection() throws SQLException {

if(poolList.size()<=0){

for(int i = 0;i<5; i++){

Connection conn = DriverManager.getConnection("jdbc:mysql:///day11","root","root");

poolList.add(conn);

}

}

final Connection conn = poolList.remove(0);

Connection procxy = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){

 

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

if("close".equals(method.getName())){

poolList.add(conn);

System.out.println("还回了一个连接,还剩余"+poolList.size());

return null;

}else{

return method.invoke(conn, args);

}

}

 

});

System.out.println("取走了一个连接,还剩余"+poolList.size());

return procxy;

}

 

public void retConn(Connection conn){

try {

if(conn == null || conn.isClosed())return;

poolList.add(conn);

System.out.println("还回了一个连接,还剩余"+poolList.size());

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

 

}

 

public Connection getConnection(String username, String password)

throws SQLException {

// TODO Auto-generated method stub

return null;

}

 

public PrintWriter getLogWriter() throws SQLException {

// TODO Auto-generated method stub

return null;

}

 

public int getLoginTimeout() throws SQLException {

// TODO Auto-generated method stub

return 0;

}

 

public void setLogWriter(PrintWriter out) throws SQLException {

// TODO Auto-generated method stub

 

}

 

public void setLoginTimeout(int seconds) throws SQLException {

// TODO Auto-generated method stub

 

}

 

public boolean isWrapperFor(Class<?> iface) throws SQLException {

// TODO Auto-generated method stub

return false;

}

 

public <T> T unwrap(Class<T> iface) throws SQLException {

// TODO Auto-generated method stub

return null;

}

 

}

 

 

package com.itheima.pool;

 

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

 

import org.junit.Test;

 

public class PoolDemo1 {

@Test

public void test1(){

MyPool pool = new MyPool();

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try{

conn = pool.getConnection();

ps = conn.prepareStatement("select * from account where id=?");

ps.setInt(1, 1);

rs = ps.executeQuery();

while(rs.next()){

String name = rs.getString("name");

double money = rs.getDouble("money");

System.out.println(name+":"+money);

}

}catch (Exception e) {

e.printStackTrace();

}finally{

if(rs != null){

try {

rs.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

rs = null;

}

}

if(ps != null){

try {

ps.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

ps = null;

}

}

if(conn!=null){

try {

conn.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

conn = null;

}

}

}

}

}

3编写数据库连接池核心

1扩展Connectionclose方法

在关闭数据库连接时,将connection存回连接池中,而并非真正的关闭

2扩展类的三种方式

基于继承--- 方法覆盖

使用装饰模式包装类,增强原有行为

使用动态代理 --- 基于字节码Class在内存中执行过程

package com.itheima.pool;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

interface 动物 {

public void ();

public void ();

}

interface xx{

public void sss();

}

public class 狗 implements 动物 ,xx {

public void (){

System.out.println("狗在叫");

}

public void (){

System.out.println("狗在吃");

}

 

//代理设计模式

public static void main(String[] args) {

final 狗 dog = new ();

动物 proxy = (动物)Proxy.newProxyInstance(dog.getClass().getClassLoader(), dog.getClass().getInterfaces()

, new InvocationHandler(){

 

public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {

if("".equals(method.getName())){

System.out.println("代理狗在旺旺叫~~~~~~~~~~");

return null;

}else{

return method.invoke(dog, args);

}

}

 

});

 

proxy.();

proxy.();

((xx)proxy).sss();

 

// dog.();

// dog.();

}

public void sss() {

System.out.println("sss");

}

}

 

 

 

//装饰设计模式

class 装饰狗 implements 动物{

private 狗 dog = null;

public 装饰狗(狗 dog) {

this.dog = dog;

}

 

public void () {

System.out.println("装饰狗在叫!!!!!");

}

 

public void () {

dog.();

}

 

}

 

//继承方式改变方法的功能

class 继承狗 extends {

@Override

public void () {

System.out.println("继承狗在叫");

}

}

4、动态代理增强close示例

使用动态代理技术构建连接池中的connection

proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()

.getClassLoader(), conn.getClass().getInterfaces(),

new InvocationHandler() {

//此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行

public Object invoke(Object proxy, Method method,

  Object[] args) throws Throwable {

if (method.getName().equals("close")) {

pool.addLast(conn);

return null;

}

return method.invoke(conn, args);

}

});

5开源数据库连接池(DataSource)

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

2也有一些开源组织提供了数据源的独立实现:

DBCP 数据库连接池 

C3P0 数据库连接池

Apache Tomcat内置的连接池(apache dbcp

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

4原来由jdbcUtil创建连接,现在由dataSource创建连接,为实现不和具体数据为绑定,因此datasource也应采用配置文件的方法获得连接。

6、DBCP数据源

1DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:

Commons-dbcp.jar:连接池的实现

Commons-pool.jar:连接池实现的依赖库

2Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用

BasicDataSource dataSource = new BasicDataSource();

dataSource.setDriverClassName("com.mysql.jdbc.Driver");

dataSource.setUrl("jdbc:mysql:///day13");

dataSource.setUsername("root");

tPassword("root");

Properties prop = new Properties();

prop.load(new FileInputStream("config.properties"));

DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);

(3)使用DBCP示例代码

static{

InputStream in = JdbcUtil.class.getClassLoader().

getResourceAsStream("dbcpconfig.properties");

Properties prop = new Properties();

prop.load(in);

 

BasicDataSourceFactory factory = new BasicDataSourceFactory();

dataSource = factory.createDataSource(prop);

}

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql:///day11

username=root

password=root

package com.itheima.pool;

 

import java.io.FileReader;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;

 

import javax.sql.DataSource;

 

import org.apache.commons.dbcp.BasicDataSourceFactory;

import org.junit.Test;

 

public class DBCPDemo {

@Test

public void test1(){

 

// BasicDataSource dataSource = new BasicDataSource();

// dataSource.setDriverClassName("com.mysql.jdbc.Driver");

// dataSource.setUrl("jdbc:mysql:///day11");

// dataSource.setUsername("root");

// dataSource.setPassword("root");

 

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try{

Properties prop = new Properties();

prop.load(new FileReader("dbcpconfig.properties"));

DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);

 

conn = dataSource.getConnection(); 

ps = conn.prepareStatement("select * from account where id=?");

ps.setInt(1, 1);

rs = ps.executeQuery();

while(rs.next()){

String name = rs.getString("name");

double money = rs.getDouble("money");

System.out.println(name+":"+money);

}

}catch (Exception e) {

e.printStackTrace();

}finally{

if(rs != null){

try {

rs.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

rs = null;

}

}

if(ps != null){

try {

ps.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

ps = null;

}

}

if(conn!=null){

try {

conn.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

conn = null;

}

}

}

}

}

 

 

 

(4)配置文件写法说明

#连接设置

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/jdbc

username=root

password=

#<!-- 初始化连接 -->

initialSize=10

#最大连接数量

maxActive=50

#<!-- 最大空闲连接 -->

maxIdle=20

#<!-- 最小空闲连接 -->

minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->

maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 

#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。

connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由连接池所创建的连接的自动提交(auto-commit)状态。

defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。

#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE

defaultTransactionIsolation=READ_UNCOMMITTED

5、C3P0 数据源

 

 

 

 

 

 

 

ComboPooledDataSource dataSource = new ComboPooledDataSource();

ComboPooledDataSource dataSource = new ComboPooledDataSource("mySoruce");

<?xml version="1.0"?>

<c3p0-config>

  <default-config>

<property  name="driverClass">com.mysql.jdbc.Driver</property >

<property name="jdbcUrl">jdbc:mysql:///Day12</property >

<property name="user">root</property>

<property name="password">root</property>

  </default-config>

  <!-- This app is massive! -->

  <named-config name="mySoruce"> 

  <property  name="driverClass">com.mysql.jdbc.Driver</property >

<property name="jdbcUrl">jdbc:mysql:///Day12</property >

<property name="user">root</property>

<property name="password">root</property>

  </named-config>

</c3p0-config>

driverClass

jdbcUrl

user

password

acquireIncrement:当连接池中已经没有连接时,连接池自动获取连接时一次获取的连接个数。

initialPoolSize:连接池初始化时,获取连接的个数。

maxPoolSize:连接池可以保有的最大的连接的数量。

maxIdleTime:当连接空闲多久时释放连接。如果该时间值设置问为0,表示从不释放连接。

minPoolSize:连接池应该保有的最小的连接的数量。

package com.itheima.pool;

 

import java.beans.PropertyVetoException;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

 

import org.junit.Test;

 

import com.mchange.v2.c3p0.ComboPooledDataSource;

 

public class C3P0Demo {

@Test

public void test() throws PropertyVetoException{

ComboPooledDataSource dataSource = new ComboPooledDataSource();

Connection conn = null;

PreparedStatement ps = null;

ResultSet rs = null;

try{

conn = dataSource.getConnection(); 

ps = conn.prepareStatement("select * from account where id=?");

ps.setInt(1, 1);

rs = ps.executeQuery();

while(rs.next()){

String name = rs.getString("name");

double money = rs.getDouble("money");

System.out.println(name+":"+money);

}

}catch (Exception e) {

e.printStackTrace();

}finally{

if(rs != null){

try {

rs.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

rs = null;

}

}

if(ps != null){

try {

ps.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

ps = null;

}

}

if(conn!=null){

try {

conn.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

conn = null;

}

}

}

}

}

 

<?xml version="1.0" ?>

<c3p0-config>

  <default-config>

  <property name="driverClass">com.mysql.jdbc.Driver</property>

  <property name="jdbcUrl">jdbc:mysql:///day11</property>

  <property name="user">root</property>

  <property name="password">root</property>

  </default-config>

  <named-config name="c1"> 

   <property name="driverClass">com.mysql.jdbc.Driver</property>

<property name="jdbcUrl">jdbc:mysql:///day10</property>

<property name="user">root</property>

<property name="password">root</property>

  </named-config>

    <named-config name="c2"> 

   <property name="driverClass">com.mysql.jdbc.Driver</property>

<property name="jdbcUrl">jdbc:mysql:///day09</property>

<property name="user">root</property>

<property name="password">root</property>

  </named-config>

</c3p0-config>

 

6、配置Tomcat数据源

查看Tomcat文档,示例代码:

<Context>

  <Resource name="jdbc/datasource" auth="Container"

            type="javax.sql.DataSource" username="root" password="root"

            driverClassName="com.mysql.jdbc.Driver" 

      url="jdbc:mysql://localhost:3306/jdbc"

            maxActive="8" maxIdle="4"/>

</Context>

Context initCtx = new InitialContext();

Context envCtx = (Context) initCtx.lookup("java:comp/env");

dataSource = (DataSource)envCtx.lookup("jdbc/datasource");

特别提醒:此种配置下,驱动jar文件需放置在tomcatlib

只有在servlet/jsp中才可以使用jndi机制

操作步骤

1、配置使用tomcat 内置连接池 配置<context> 元素 

context元素有三种常见配置位置

1) tomcat/conf/context.xml 所有虚拟主机,所有工程都可以访问该连接池 

2) tomcat/conf/Catalina/localhost/context.xml 当前虚拟主机(localhost)下所有工程都可以使用该连接池

3) 当前工程/META-INF/context.xml 只有当前工程可以访问该连接池 

<Context>

  <Resource name="jdbc/EmployeeDB" auth="Container"

            type="javax.sql.DataSource" username="root" password="abc"

            driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"

            maxActive="8" maxIdle="4"/>

</Context>

必须先将mysql驱动jar包 复制tomcat/lib下 

tomcat启动服务器时,创建连接池对象,绑定 jdbc/EmployeeDB 指定名称上 

2、通过运行在JNDI容器内部的程序(Servlet/JSP)去访问tomcat内置连接池 

Context context = new InitialContext();

Context envCtx = (Context)context.lookup("java:comp/env"); 固定路径

DataSource datasource = (DataSource) envCtx.lookup("jdbc/EmployeeDB"); 通过绑定名称,查找指定java对象 

=====================================================================补充知识:

Context中配置数据源,有五个位置可以配置,参考tomcat文档

~conf/context.xml爸爸Context中配置,这个配置将被tomcat的所有web应用共享

In the $CATALINA_BASE/conf/context.xml file: the Context element information will be loaded by all webapps.

~conf/Catalina/localhost/context.xml,这个配置将被当前虚拟主机所共享

In the $CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default file: the Context element information will be loaded by all webapps of that host.

~conf/Catalina/localhost/XXXXXX.xml,这是配置web应用的对外访问路径,这个配置只对当前web应用起作用

In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The name of the file (less the .xml extension) will be used as the context path. Multi-level context paths may be defined using #, e.g. foo#bar.xml for a context path of /foo/bar. The default web application may be defined by using a file called ROOT.xml.

~web应用个的META-INF目录下创建context.xml这个配置只对当前web应用起作用

Only if a context file does not exist for the application in the $CATALINA_BASE/conf/[enginename]/[hostname]/, in an individual file at /META-INF/context.xml inside the application files. If the web application is packaged as a WAR then /META-INF/context.xml will be copied to $CATALINA_BASE/conf/[enginename]/[hostname]/ and renamed to match the application's context path. Once this file exists, it will not be replaced if a new WAR with a newer /META-INF/context.xml is placed in the host's appBase.

~conf/servler.xmlHost标签下配置Context标签

Inside a Host element in the main conf/server.xml.

4Cotext中配置:

  <Resource name="mySource"  ---在数据源创建好以后绑定到jndi容器中时使用的名字

            auth="Container" 

            type="javax.sql.DataSource" ---当前对象的类型,默认就是数据源

            username="root"  --- 数据库用户名

            password="root"  --- 数据库密码

            driverClassName="com.mysql.jdbc.Driver" ---数据库驱动名

            url="jdbc:mysql:///day12" --- 数据库连接信息

            maxActive="8" --- 最大连接数

            maxIdle="4"/> --- 最大空闲连接数

 5在程序中获取数据源:

  Context context = new InitialContext();

Context envCtx = (Context)context.lookup("java:comp/env"); 

DataSource datasource = (DataSource) envCtx.lookup("mySource");

7JNDI技术简介

1JNDI(Java Naming and Directory Interface)Java命名和目录接口,它对应于J2SE中的javax.naming包,

2这套API的主要作用在于:它可以把Java对象放在一个容器中(支持JNDI容器 Tomcat),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。

3其核心APIContext,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

4原来由jdbcUtil创建连接,现在由dataSource创建连接,为实现不和具体数据为绑定,因此datasource也应采用配置文件的方法获得连接。

Copyright © 2024 冰天雪域
Powered by .NET 9.0 on Kubernetes