java常用连接池_Java 学习使用常见的开源连接池

目录

连接池介绍

在说连接池之前,我们先想一个问题:程序要进行数据库操作,与数据库建立的是什么连接?开销怎么样?数据库是否可以同时支持上百万个连接?

首先第一个问题:程序与数据库建立的是socket连接,走的是传输层,使用TCP。

第二个问题:总开销 约等于 程序运行耗时 + 网络io + 数据库运行耗时。

第三个问题:应该是不能同时支持上百万个连接,几千个应该就是上限了。

看了上面三个问题,再提出一个问题:上面的问题中,我们可以优化哪一个环节?

运维可以优化网络传输的问题;DBA可以优化数据库的运行性能。

对于开发人员来说,我们可以优化第一个问题;首先,创建socket连接真的很耗时,主要的原因是因为建立TCP连接时有个3次握手,建立连接之后传输数据开销其实并不大。所以我们可以在这个角度上进行优化:尽量让程序与数据库建立几个连接,不要让程序频繁与数据库建立连接。

为了业务的正常执行,线程需要与数据库进行交互,只建立几个数据库连接,好像不现实。

我们可以换个方式来优化:创建固定数量的数据库的连接,这些数据库连接在Java中就是一个个对象而已,我们可以将这些对象存到容器中(比如List中),这个容器就叫做连接池。

当有一个线程需要与数据库交互的时候,如果容器中还有数据库连接对象,那就从容器中取出一个连接对象,使用完之后,并不关闭数据库连接,而是将数据库连接对象放回容器,方便其他线程使用;如果线程需要与数据库交互时,容器中没有数据库连接对象了,那么这个线程他就阻塞一下,等待别的线程使用完连接之后归还,然后再使用别的线程归还的数据库连接。

自定义连接池

自定义连接池的有个规范,需要实现DataSource接口,并且重写多个方法,但是主要重写一个无参的getConnection方法:

package cn.ganlixin.utils;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintWriter;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.sql.SQLFeatureNotSupportedException;

import java.util.Collections;

import java.util.LinkedList;

import java.util.List;

import java.util.Properties;

import java.util.logging.Logger;

import javax.sql.DataSource;

/**

* 自定义的超简单的连接池,主要重写了DataSource接口中的getConnection方法,然后创建了归还连接的方法

*/

public class MyConnectionPool implements DataSource {

// 创建一个同步的容器来保存数据库连接

private static List connectionList =

Collections.synchronizedList(new LinkedList());

// 设置建立数据库连接的最大数

private static final int MAX_CONNECTIONS = 50;

// 编写静态初始化块,读取数据库配置文件,建立数据库连接,放入容器

static {

InputStream _is = MyConnectionPool.class.getClassLoader().getResourceAsStream("database.properties");

try {

Properties props = new Properties();

props.load(_is);

String driver = props.getProperty("jdbc.driver");

String url = props.getProperty("jdbc.url");

String username = props.getProperty("jdbc.username");

String password = props.getProperty("jdbc.password");

Class.forName(driver);

// 创建多个连接,放入容器中

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

Connection conn = DriverManager.getConnection(url, username, password);

connectionList.add(conn);

System.out.println("创建第 " + (i+1) + " 个连接");

}

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (SQLException e) {

e.printStackTrace();

}

}

// 从容器中取出一个数据库连接对象

@Override

public Connection getConnection() throws SQLException {

if (connectionList.size() > 0) {

// 如果容器中还有数据库连接对象,就将取出第一个对象,并将该对象从容器中删除

Connection conn = connectionList.remove(0);

System.out.println("使用了一个数据库连接, 容器中还剩下 " + connectionList.size() + " 个数据库连接");

return conn;

}

return null;

}

/**

* 归还数据库连接给容器,便于其他线程使用

* @param conn 要归还的数据库连接对象

*/

public void releaseConnection(Connection conn) {

connectionList.add(conn);

}

@Override

public PrintWriter getLogWriter() throws SQLException { return null; }

@Override

public void setLogWriter(PrintWriter out) throws SQLException {}

@Override

public void setLoginTimeout(int seconds) throws SQLException {}

@Override

public int getLoginTimeout() throws SQLException { return 0; }

@Override

public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; }

@Override

public T unwrap(Class iface) throws SQLException { return null; }

@Override

public boolean isWrapperFor(Class> iface) throws SQLException { return false; }

@Override

public Connection getConnection(String username, String password) throws SQLException { return null; }

}

测试

package cn.ganlixin.test;

import java.sql.Connection;

import java.sql.SQLException;

public class TestPool {

public static void main(String[] args) throws SQLException {

MyConnectionPool connectionPool = new MyConnectionPool(); // 获取连接池

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

// 获取连接

Connection connection = connectionPool.getConnection();

if (i % 3 == 0) { // 归还连接

connectionPool.releaseConnection(connection);

}

System.out.println(connection);

}

}

}

JDBC Tomcat Pool

tomcat服务器可以提供数据库连接池。我们可以将数据库连接池的配置保存在一个文件名为context.xml的文件中。

context.xml可以放在项目下的webRoot/META-INF目录下,只对于本项目有效。

context.xml可以放在tomcat服务器安装路径的conf目录下,针对所有项目都有效。

WEB-INF/web.xml

driverClassName="com.mysql.jdbc.Driver"

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

user="root"

password="123456"

name="tomcat_supported_pool"

auth="Container"

maxActive="50"

maxIdle="20"

maxWait="10000"

type="javax.sql.DataSource"

>

测试:

package lixin.gan.test;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.sql.DataSource;

/**

* Servlet implementation class TestPool

*/

@WebServlet("/TestPool")

public class TestPool extends HttpServlet {

@Override

public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {

request.setCharacterEncoding("utf-8");

response.setContentType("text/html; charset=utf-8");

try {

Context ctx = new InitialContext();

DataSource ds = (DataSource) ctx.lookup("java:comp/env/tomcat_supported_pool");

Connection conn = ds.getConnection();

/*

进行数据库操作即可

*/

} catch (NamingException e) {

e.printStackTrace();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

DBCP(DataBase Connection Pool)

需要下载commons-dbcp.jar、commons-pool.jar、commons-logging.jar。另外仍旧需要导入mysql的驱动包。

下载网址(下载xxx-bin.zip即可):

下载之后解压,将jar包添加到build path中。

使用DBCP的示例:

package cn.ganlixin.test;

import java.sql.Connection;

import java.sql.SQLException;

import java.sql.Statement;

import org.apache.commons.dbcp2.BasicDataSource;

public class TestDBCP {

public static void main(String[] args) throws SQLException {

// 获取连接池

BasicDataSource dataSource = new BasicDataSource();

// 设置连接池相关信息

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

dataSource.setUrl("jdbc:mysql://localhost:3306/test");

dataSource.setUsername("root");

dataSource.setPassword("root");

dataSource.setInitialSize(5);

dataSource.setMinIdle(2);

// 获取数据库连接

Connection conn = dataSource.getConnection();

// 执行数据库操作

Statement stmt = conn.createStatement();

stmt.executeUpdate("update stu set age = 30 where id < 4");

// 这个close()方法被重写了,并不是关闭数据库连接,而是将连接归还连接池

conn.close();

}

}

使用配置文件来设置DBCP

前面使用DBCP时,是在程序中手动指定连接信息,同样的,我们可以使用配置文件保存DBCP的连接信息。

操作:在src下创建一个dbcp.properties文件,内容如下:

driverClassName=com.mysql.jdbc.Driver

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

username=root

password=root

initialSize=20

maxIdle=10

minIdle=5

maxWait=10000

使用DBCP

package cn.ganlixin.test;

import java.io.InputStream;

import java.sql.Connection;

import java.sql.Statement;

import java.util.Properties;

import org.apache.commons.dbcp2.BasicDataSource;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

public class TestPool {

public static void main(String[] args) throws Exception {

BasicDataSourceFactory dataSourceFacoty = new BasicDataSourceFactory();

InputStream _is = TestPool.class.getClassLoader().getResourceAsStream("dbcp.properties");

Properties props = new Properties();

props.load(_is);

// 获取连接池

BasicDataSource dataSource = dataSourceFacoty.createDataSource(props);

Connection conn = dataSource.getConnection();

Statement stmt = conn.createStatement();

stmt.executeUpdate("update stu set age = 30 where id < 4");

conn.close();

}

}

DBCP返回的数据库连接是包装类对象

当我们从DBCP连接池获取的连接其实是一个经过包装之后的数据库连接对象,而不是原生的jdbc数据库连接对象:

package cn.ganlixin.test;

import java.io.InputStream;

import java.sql.Connection;

import java.util.Properties;

import org.apache.commons.dbcp2.BasicDataSource;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import org.apache.commons.dbcp2.DelegatingConnection;

public class TestDBCP1 {

public static void main(String[] args) throws Exception {

BasicDataSourceFactory dataSourceFacoty = new BasicDataSourceFactory();

InputStream _is = TestPool.class.getClassLoader().getResourceAsStream("dbcp.properties");

Properties props = new Properties();

props.load(_is);

BasicDataSource dataSource = dataSourceFacoty.createDataSource(props);

// 获取数据库连接

Connection conn = dataSource.getConnection();

System.out.println(conn.getClass().getName());

// org.apache.commons.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper

// 通过DBCP连接池获得的是一个包装过后的连接池对象

// 可以通过下面的步骤获取原生的Connection对象

DelegatingConnection dc = (DelegatingConnection) conn;

Connection connection = dc.getInnermostDelegateInternal();

System.out.println(connection.getClass().getName());

// com.mysql.jdbc.JDBC4Connection

}

}

C3P0

C3P0也是一个数据库连接池的开源项目,他和DBCP功能类似,但是有一些区别:

1、DBCP不会自动回收空闲连接的功能,而C3P0有这个功能。

2、DBCP需要手动指定配置文件的路径以及文件明,而C3P0不需要(C3P0的配置文件指定路径和名称)。

需要下载c3p0.jar,mchange-commons-java.jar,以及mysql的驱动包。

创建C3P0的配置文件

C3P0的配置文件名为c3p0-config.xml,放在src目录下即可,配置文件的内容如下:

com.mysql.jdbc.Driver

jdbc:mysql://localhost:3306/test

root

root

30

60

60

15

测试C3P0

package cn.ganlixin.test;

import java.sql.Connection;

import java.sql.SQLException;

import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class TestC3P0 {

public static void main(String[] args) throws SQLException {

// 获取连接池

// 加载默认的数据库连接配置

// ComboPooledDataSource dataSource = new ComboPooledDataSource();

// 使用c3p0配置文件中named-config里面name为dev的数据库配置

ComboPooledDataSource dataSource = new ComboPooledDataSource("dev");

// 获取数据库连接

Connection connection = dataSource.getConnection();

// 获取到数据库连接之后就可以进行各种操作了

// code

// c3p0返回的数据库连接同样是包装类

System.out.println(connection);

// com.mchange.v2.c3p0.impl.NewProxyConnection@769c9116

// [wrapping: com.mysql.jdbc.JDBC4Connection@6aceb1a5]

// 将数据库连接归还连接池

connection.close();

}

}

Druid

Druid是alibaba开源的一个连接池项目;

使用Druid,需要下载druid.jar以及mysql的驱动包;

使用方法设置连接池信息

package cn.ganlixin.test;

import java.sql.SQLException;

import com.alibaba.druid.pool.DruidDataSource;

import com.alibaba.druid.pool.DruidPooledConnection;

public class TestDruid {

public static void main(String[] args) throws SQLException {

// 获取连接池对象

DruidDataSource dataSource = new DruidDataSource();

// 设置连接信息

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

dataSource.setUrl("jdbc:mysql://localhost:3306/test");

dataSource.setUsername("root");

dataSource.setPassword("root");

// 设置连接池的配置

dataSource.setInitialSize(20);

dataSource.setMaxActive(30);

dataSource.setMaxWait(1000);

dataSource.setMinIdle(10);

// 获取连接(得到的不是原生的jdbc for mysql的连接对象)

DruidPooledConnection connection = dataSource.getConnection();

// 释放连接到连接池中

connection.close();

}

}

使用properties配置文件的方式配置Druid

在src下创建druid-config.properties(文件名随意),内容如下:

driverClassName=com.mysql.jdbc.Driver

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

username=root

password=root

maxActive=10

initialSize=5

maxWait=10000

minIdle=5

配置项和DBCP几乎一样。

进行测试:

package cn.ganlixin.test;

import java.io.InputStream;

import java.sql.Connection;

import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class TestDruid2 {

public static void main(String[] args) throws Exception {

InputStream _is = TestDruid2.class.getClassLoader().getResourceAsStream("druid-config.properties");

Properties props = new Properties();

props.load(_is);

/**

读取配置文件,手动调用setter进行设置连接池

DruidDataSource dataSource = new DruidDataSource();

dataSource.setDriverClassName(props.getProperty("driverClassName"));

.........

*/

// 利用工厂加载配置文件来配置连接池

DruidDataSourceFactory factory = new DruidDataSourceFactory();

DataSource dataSource = factory.createDataSource(props);

// 获取数据库连接

Connection connection = dataSource.getConnection();

System.out.println(connection.getClass().getName());

connection.close();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值