天天看点

常见的连接池-DBCP、C3P0、Druid什么是连接池常见的连接池

文章目录

  • 什么是连接池
  • 常见的连接池
    • DBCP
    • C3P0
      • CP30和DBCP的区别
    • DBUtils工具类
    • Druid
    • Druid配置加密过后的数据库密码
      • 公钥和密钥
      • Druid加密步骤

什么是连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

返回顶部

常见的连接池

  • DBCP
  • C3P0
  • Druid

下面我们分别来演示:

返回顶部

DBCP

DBCP(DataBase connection pool)数据库连接池。是 Apache 上的一个 Java 连接池项目,也是 tomcat 使用的连接池组件。单独使用DBCP需要2个包:

  • commons-dbcp.jar
  • commons-pool.jar。

资源在这里,有需要的朋友可以下载:

https://pan.baidu.com/s/1YIO7NX57XeGpetLnSLTP-g

提取码:js8u

注意,导入这两个jar包之前,要先导入数据库驱动jar包。

假如现在mysql数据库中有0319test这么一个库,库中有一个bank表:

CREATE DATABASE 0319test;

CREATE TABLE bank(
    username VARCHAR(32),
    money DOUBLE
);

INSERT INTO bank VALUES("张三",5000);
INSERT INTO bank VALUES("李四",5000);
-- 这是本来已经有的两条数据
username    money  
---------  --------
张三             5000
李四             5000
           

我们现在通过DBCP来连接0319test库,并更新bank表中的数据:

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;

public class DBCPDemo {
    public static void main(String[] args) throws Exception {
        //    第一步:导入DBCP的两个jar包,复制粘贴在project下新建的lib文件夹下,然后右键点击Add as Library依赖一下
        //    第二部:使用
        /*有两种使用方式:
         * 1、硬编码的方式,所有的数据库需要的参数或者连接池所需要的参数都用代码设置
         * 2、将配置缠住单独写在配置文件中*/
        //方式1
        test1();
        //方式2
        test2();
    }

    private static void test1() throws SQLException {
        //    1.创建连接池对象
        BasicDataSource bds = new BasicDataSource();
        //    2.设置参数
        //    url格式: "jdbc:mysql://ip地址:端口/库名",本地连接可简写如下
        bds.setUrl("jdbc:mysql:///0319test");
        bds.setUsername("root");
        bds.setPassword("123456");
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        //    3.从连接池中获取连接对象
        Connection conn = bds.getConnection();
        //    sql语句
        String sql = "insert into bank values(?,?) ";
        //    4.获取操作对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //    给参数赋值
        ps.setString(1, "陆雪琪");
        ps.setDouble(2, 20000);
        //    5.执行
        ps.executeUpdate();
        //    6.释放资源-释放连接对象即可
        bds.close();
    }

	 private static void test2() throws Exception {
        //加载配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("dbcp.properties"));
        //1.读取配置文件获取连接池对象
        DataSource ds = new BasicDataSourceFactory().createDataSource(properties);
        //2.从连接池对象中获取连接对象
        Connection conn = ds.getConnection();
        //sql语句
        String sql = "insert into bank values(?,?)";
        //3.获取操作对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //给参数赋值
        ps.setString(1, "碧瑶");
        ps.setDouble(2, 20000);
        //4.执行
        ps.executeUpdate();
        //5.释放资源
        conn.close();
    }
}

           
-- 加入陆雪琪用户成功
-- 加入碧瑶用户成功
username    money  
---------  --------
张三             5000
李四             5000
陆雪琪           20000
碧瑶             20000
           

这是原始的配置文件dbcp.properties,上面分享的资源里面有源文件,我们可以在这里更改连接参数,CSDN博客好像不支持properties类型的代码,就贴成图片了,看起来方便些:

常见的连接池-DBCP、C3P0、Druid什么是连接池常见的连接池

返回顶部

C3P0

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。和DBCP一样,使用之前都要导入数据库驱动jar包和C3P0jar包:

链接:https://pan.baidu.com/s/1hlKZdLNuxJPY1SuOefkqYw

提取码:0y2l

下面我们尝试用C3P0来连接数据库0319test并且给表bank中添加数据:

package org.westos.demo2;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.*;

public class C3P0Demo {
    public static void main(String[] args) throws Exception {
        //和DBCP一样,C3P0也有两种使用方式:
        //1.硬解码
        test1();
        //2.配置文件
        test2();
    }
    
    private static void test1() throws Exception {
        //1.获取连接池对象
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        //2.设置连接对象参数
        cpds.setJdbcUrl("jdbc:mysql:///0319test");
        cpds.setUser("root");
        cpds.setPassword("123456");
        cpds.setDriverClass("com.mysql.jdbc.Driver");
        //3.获取连接对象
        Connection conn = cpds.getConnection();
        //4.获取操作对象
        String sql = "insert into bank values(?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "大黄蜂");
        ps.setDouble(2, 20000);
        //5.执行语句
        ps.executeUpdate();
        //6.释放资源
        conn.close();
    }

  private static void test2() throws SQLException {
        //C3P0:使用配置文件的方式,有两点要求:
        //1.配置文件的文件名不能修改
        //2.配置文件要放在src目录下
        //使用c3p0.xml配置文件,可以设置第二配置
        //如果有需要使用第二配置,就把第二配置的配置名写上
        //不传参数就使用默认参数
        ComboPooledDataSource cpds = new ComboPooledDataSource("second_config");
        Connection conn = cpds.getConnection();
        String sql = "insert into bank values(?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "红蜘蛛");
        ps.setDouble(2, 20000);
        ps.executeUpdate();
        conn.close();
    }
}

           

更新后的bank表:

username    money  
---------  --------
张三             5000
李四             5000
陆雪琪           20000
碧瑶             20000
大黄蜂           20000
红蜘蛛           20000
           

c3p0.xm配置文件:

C3P0:使用配置文件的方式,有两点要求:

1.配置文件的文件名不能修改

2.配置文件要放在src目录下

使用c3p0.xml配置文件,可以设置第二配置

<c3p0-config>
	<!-- 默认配置,如果没有指定则使用这个配置 -->
	<default-config>
		<!-- 基本配置 -->
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///0319test</property>
		<property name="user">root</property>
		<property name="password">123456</property>
	
		<!--扩展配置-->
		<property name="checkoutTimeout">30000</property>
		<property name="idleConnectionTestPeriod">30</property>
		<property name="initialPoolSize">10</property>
		<property name="maxIdleTime">30</property>
		<property name="maxPoolSize">100</property>
		<property name="minPoolSize">10</property>
		<property name="maxStatements">200</property>
	</default-config> 
	
	
	<!-- 命名的配置 -->
	<named-config name="second_config">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///0319test</property>
		<property name="user">root</property>
		<property name="password">123456</property>
		
		
		<!-- 如果池中数据连接不够时一次增长多少个 -->
		<property name="acquireIncrement">5</property>
		<property name="initialPoolSize">20</property>
		<property name="minPoolSize">10</property>
		<property name="maxPoolSize">40</property>
		<property name="maxStatements">20</property>
		<property name="maxStatementsPerConnection">5</property>
	</named-config>
</c3p0-config> 

           

返回顶部

CP30和DBCP的区别

dbcp没有自动回收空闲连接的功能,c3p0有自动回收空闲连接功能。

返回顶部

DBUtils工具类

这是jar包:

链接:https://pan.baidu.com/s/1TesHTfFSyP9BZMlroGKEHw

提取码:j1xf

这是官方API:

http://commons.apache.org/proper/commons-dbutils/

关于DBUtils的知识,这里有更详细的介绍:

https://www.jianshu.com/p/10241754cdd7

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,创建连接、结果集封装、释放资源,同时也不会影响程序的性能。

这是几个核心类:

  • DbUtils类:

    提供如加载驱动、关闭连接、事务提交、回滚等常规工作的工具类,里面的所有方法都是静态的

  • QueryRunner类:

    该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

    • 默认的构造方法:QueryRunner()
    • 需要一个 javax.sql.DataSource 来作参数的构造方法:QueryRunner(DataSource ds)
  • ResultSetHandler接口:

    ResultSetHandler 接口的实现类(构造方法不唯一,在这里只用最常见的构造方法):

    1. ArrayHandler():把结果集中的第一行数据转成对象数组(存入Object[])。
    2. ArrayListHandler():把结果集中的每一行数据都转成一个对象数组,再存放到List中。
    3. BeanHandler(Class type):将结果集中的第一行数据封装到一个对应的JavaBean实例中。
    4. BeanListHandler(Class type):将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。

案例演示1:

import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws Exception {
    	//构造QueryRunner时需要传入一个DataSource,即数据源
    	
        //构造数据源方式一:手动设置参数
        //BasicDataSource bds = new BasicDataSource();
        //bds.setUrl("jdbc:mysql:///0319test");
        //bds.setUsername("root");
        //bds.setPassword("123456");
        //bds.setDriverClassName("com.mysql.jdbc.Driver");
        
        //构造数据源方式二:读取配置文件
        //DBCP的配置文件要自己手动获取,把写好的配置文件放在src目录下
        Properties properties = new Properties();
        properties.load(new FileInputStream("./src/dbcp.properties"));
        DataSource ds = BasicDataSourceFactory.createDataSource(properties);
        //构造QueryRunner,传入数据源
        QueryRunner queryRunner = new QueryRunner(ds);
        String sql = "insert into bank values(?,?)";
        int i = queryRunner.update(sql, "李四", 1000);
        if (i > 0) {
            System.out.println("插入成功");
        } else {
            System.out.println("插入失败");
        }
    }
}

           

案例演示2:把查出的数据封装到集合里面去

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

public class DBUtilsDemo {
    public static void main(String[] args) throws SQLException {
    	//DataSources接口,第三方连接池只要实现了这个接口,就可以作为DBUtils的数据源
    	//这里用C3P0来演示,C3P0貌似可以自动读取放置在src目录下的配置文件
    	//但是DBCP需要自己手动读取配置文件
    	//不管怎样,构造QueryRunner时需要传入一个DataSource,即数据源
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        QueryRunner queryRunner = new QueryRunner(cpds);
        String sql = "select * from bank";
        //将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
        List<Users> list = queryRunner.query(sql, new BeanListHandler<Users>(Users.class));
        System.out.println(list);
        cpds.close();
    }
}
/*[Users{username='张三', money=5000}, Users{username='李四', money=5000}, 
Users{username='陆雪琪', money=20000}, Users{username='碧瑶', money=20000}, 
Users{username='大黄蜂', money=20000}, Users{username='大黄蜂', money=20000}, 
Users{username='红蜘蛛', money=20000}]
 */
           

Users类:

public class Users{
    private String username;
    private int money;

    public Users() {
    }

    public Users(String username, int money) {
        this.username = username;
        this.money = money;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Users{" +
                "username='" + username + '\'' +
                ", money=" + money +
                '}';
    }
}

           

返回顶部

Druid

Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource,号称史上最强大。

链接:https://pan.baidu.com/s/1jfJOF7MpWr0hU-FmK1V-fg

提取码:fxoq

  • Druid的功能
  1. 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
  2. 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
  3. 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
  4. SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
  5. 扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

public class DruidDemo {
    public static void main(String[] args) throws Exception {
        test1();
        test2();
    }

    private static void test1() throws SQLException {
        //获取连接池对象
        DruidDataSource dds = new DruidDataSource();
        //设置连接参数
        dds.setUrl("jdbc:mysql:///0319test");
        dds.setUsername("root");
        dds.setPassword("123456");
        dds.setDriverClassName("com.mysql.jdbc.Driver");
        //获取连接对象
        DruidPooledConnection conn = dds.getConnection();
        //获取操作对象
        String sql = "insert into bank values(?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "Druid");
        ps.setDouble(2, 200000000);
        //执行
        ps.executeUpdate();
        //释放资源
        conn.close();
    }
    
  	private static void test2() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream("./src/db_server.properties"));
        DataSource ds = DruidDataSourceFactory.createDataSource(properties);
        Connection conn = ds.getConnection();
        String sql = "insert into bank values(?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, "Druid2");
        ps.setDouble(2, 200000000);
        ps.executeUpdate();
        conn.close();
    }
}
           

配置文件内容:

常见的连接池-DBCP、C3P0、Druid什么是连接池常见的连接池

更新后的数据:

username       	money  
---------  		-----------
张三                 5000
李四                 5000
陆雪琪               20000
碧瑶                 20000
大黄蜂               20000
大黄蜂               20000
红蜘蛛               20000
Druid        		200000000
Druid2       		200000000
           

返回顶部

Druid配置加密过后的数据库密码

上面举的例子中,我们都是直接把密码以明文的形式写再配置文件中,这样做有安全风险,我们现在来尝试把密码来加密一下。

公钥和密钥

SA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

RSA是目前最有影响力的公钥加密算法,属于非对称加密算法,即签名密钥(私钥)与验签密钥(公钥)是不一样的,私钥用于签名,公钥用于验签。它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。

这种算法加密和解密的密码不一样,一个是公钥,另一个是私钥:

  • 公钥和私钥成对出现
  • 公开的密钥叫公钥,只有自己知道的叫私钥
  • 用公钥加密的数据只有对应的私钥可以解密
  • 用私钥加密的数据只有对应的公钥可以解密
  • 如果可以用公钥解密,则必然是对应的私钥加的密
  • 如果可以用私钥解密,则必然是对应的公钥加的密
  • 公钥和私钥是相对的,两者本身并没有规定哪一个必须是公钥或私钥。

这里有相关知识的详细介绍:

https://songlee24.github.io/2015/05/03/public-key-and-private-key/

总结:公钥和私钥是成对的,它们互相解密。

公钥加密,私钥解密。

私钥数字签名,公钥验证。

返回顶部

Druid加密步骤

Druid提供了数据库密码加密的功能,下面介绍加密步骤:

  1. 找到Druid的jar包的文件路径,在该路径下执行该dos命令
  • java -cp druid-1.1.9.jar com.alibaba.druid.filter.config.ConfigTools 123456

  • druid-1.1.9.jar

    ——对应的jar包名称
  • 123456

    ——对应的数据库密码
  1. 会返回类似的一串符号

privateKey:MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAoXEOA7TSQxSiVCJuGmKbbper3P0WZIzocTbd4FAnd3YhOiOC5oYokSMxCqv7lsl/4S5/9vEpcOl1LTUhW4nd1QIDAQABAkBkUM7HiM16d1Di/L3z0UAJ+V8Go5ENi+1HpDM8ljHf7VuljuARZXDVEV3SVYVZzxAqbN8Di4E841pGLklUaAxhAiEA880TPYtM2wR+1ZDLOfaOUOsf/cMA1ZHsR0fURaIMRL0CIQCphQLoj7ysk8nTJtOWoAIw+3gRDuCZuQc0fLAWquQq+QIhAL+sBuhnz/CQxDabM2tKj/DGKcyTtuAxbRVNP3HPSN3xAiEAk4KO2VfaUkH36alrjTKHNaBdBlrvzlRXlLC7eb4S9eECIA6rQQBKI0t6gM9f0c7zQMELMXrDgvwlHLe1jE6ivKrW

publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKFxDgO00kMUolQibhpim26Xq9z9FmSM6HE23eBQJ3d2ITojguaGKJEjMQqr+5bJf+Euf/bxKXDpdS01IVuJ3dUCAwEAAQ==

password:YefGn9UBZ4W0NZJOd8/SaSTNZk1sE8YXSN+HOIKZntsOz9IWZ9Ek5dh/mA2kmh5DYJjSF6qByVLNEWi7jyDJ8A==

  • privateKey

    ——私钥
  • publicKey

    ——公钥
  • password

    ——密码

当然,你也可以在java代码里来获取这三个参数,和上面一样,我们对123456来用私钥加密:

import com.alibaba.druid.filter.config.ConfigTools;

public class DruidDemo3 {
    public static void main(String[] args) throws Exception {
       String password = "123456";
        System.out.println("密码[ " + password + " ]的加密信息如下:\n");
        //RSA keys must be at least 512 bits long,这里传入的参数>=512
        String[] keyPair = ConfigTools.genKeyPair(550);
        //获取私钥
        String privateKey = keyPair[0];
        //获取公钥
        String publicKey = keyPair[1];
        //用私钥加密ConfigTools.encrypt
        password = ConfigTools.encrypt(privateKey, password);
        //输出私钥,公钥,和私钥加密后的密码来看看
        System.out.println("privateKey:" + privateKey);
        System.out.println("publicKey:" + publicKey);
        System.out.println("password:" + password);
        //用公钥解密ConfigTools.decrypt
        String decryptPassword = ConfigTools.decrypt(publicKey, password);
        System.out.println("decryptPassword:" + decryptPassword);//123456
    }
}

           

3.现在我们来写配置文件:

我们在src下新建一个名叫druid_info.properties文件,然后把上面的公钥和密码拷贝下来:

#基本配置
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///0319test
username=root
#公钥
publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKFxDgO00kMUolQibhpim26Xq9z9FmSM6HE23eBQJ3d2ITojguaGKJEjMQqr+5bJf+Euf/bxKXDpdS01IVuJ3dUCAwEAAQ==
#密码
password:YefGn9UBZ4W0NZJOd8/SaSTNZk1sE8YXSN+HOIKZntsOz9IWZ9Ek5dh/mA2kmh5DYJjSF6qByVLNEWi7jyDJ8A==
#最大连接数
maxActive=3
           
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;

public class DruidDemo4 {
    public static void main(String[] args) throws IOException, SQLException {
        //1.创建属性集合
        Properties properties = new Properties();
        properties.load(new FileInputStream("./src/druid_info.properties"));
        //读取配置文件
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        String maxActive = properties.getProperty("maxActive");
        String publickey = properties.getProperty("publicKey");
        //2.声明DruidDataSource
        DruidDataSource dds = new DruidDataSource();
        //3.设置数据库信息
        dds.setDriverClassName(driver);
        dds.setUrl(url);
        dds.setUsername(username);
        dds.setPassword(password);
        dds.setMaxActive(Integer.parseInt(maxActive));
        //4.config:使用ConfigFilter解密,Stat:启动监控功能StatFilter
        dds.setFilters("config,stat");
        //5.设置解密使用publicKey
        dds.setConnectionProperties("config.decrypt=true;config.decrypt.key=" + publickey);
        //6.获取连接对象
        DruidPooledConnection conn = dds.getConnection();
        //我们就不进行具体操作了,来看看能不能获取到连接对象
        System.out.println(conn);
        /*[email protected]*/
        //成功,释放资源
        conn.close();
    }
}

           

返回顶部

继续阅读