连接池原理
数据库连接池负责分配、管理和释放数据库连接,它的核心思想就是连接复用,通过建立一个数据库连接池,这个池中有若干个连接对象,当用户想要连接数据库,就要先从连接池中获取连接对象,然后操作数据库。一旦连接池中的连接对象被用院了,判断连接对象的个数是否已达上限,如果没有可以再创建新的连接对象,如果已达上限,用户必须处于等待状态,等待其他用户释放连接对象,直到连接池中有被释放的连接对象了,这时候等待的用户才能获取连接对象,从而操作数据库。这样就可以使连接池中的连接得到高效、安全的复用,避免了数据库连接频繁创建、关闭的开销。这项技术明显提高对数据库操作的性能。
-
无连接池
-
有连接池
连接池优点
程序启动的时候就已经创建好了连接,不需用户请求的时候创建
用户关闭时不会销毁连接,需要将连接归还,就可以达到复用的效果
如果超过了使用的连接会进行上限的判断,如果没有达到最大值,可以继续创建
如果有空闲连接,会默认的进行销毁(释放)一些连接,让系统达到最优
手动实现连接池原理
手动实现连接池 (了解)
步骤:
创建一个连接池类,需要借助于封装的jdbcUtils工具类
初始化连接数量
创建一个集合,存储多个连接对象
创建一个从池子里面获取连接的方法
创建一个向连接池里面归还连接的方法
测试
//连接池类
public class MyDataPool {
//初始化连接数量
private static int initSize = 5;
//创建一个集合, 存储多个连接对象
private static List<Connection> list = new ArrayList<Connection>();
static {
for(int i = 0; i < initSize; i++) {
//借助于JDBCUtils工具类获取连接
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
System.out.println("初始化连接的数量为: " + initSize);
}
//获取连接
public static Connection getConnFromPool() {
//从集合中取出连接
Connection connection = list.get(0);
//还要取走的连接移除
list.remove(0);
System.out.println("用户正在使用" + connection + "连接, 当前池子中剩余连接为: " + list.size());
return connection;
}
//归还连接
public static void closeAll(ResultSet rs, PreparedStatement psvm, Connection conn) {
//判断
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(psvm != null) {
try {
psvm.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
//conn.close();
//归还
list.add(conn);
System.out.println("用户正在归还" + conn +"连接, 当前池子中剩余连接为:" + list.size());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
测试类:
package com.ujiuye.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.ujiuye.pool.MyDataPool;
public class myDataPoolTest {
public static void main(String[] args) throws Exception {
//直接获取连接
Connection conn = MyDataPool.getConnFromPool();
//归还连接
MyDataPool.closeAll(null, null, conn);
System.out.println("==========================");
Connection conn1 = MyDataPool.getConnFromPool();
String sql = "select * from user where uid = ?";
PreparedStatement psvm = conn1.prepareStatement(sql);
//设置参数
psvm.setInt(1, 1);
//执行
ResultSet rs = psvm.executeQuery();
while(rs.next()) {
int uid = rs.getInt("uid");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.println(uid + "\t" + username + "\t" + password);
}
//归还
MyDataPool.closeAll(rs, psvm, conn1);
}
}
测试类:
package com.ujiuye.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.ujiuye.pool.MyDataPool;
public class myDataPoolTest {
public static void main(String[] args) throws Exception {
//直接获取连接
Connection conn = MyDataPool.getConnFromPool();
//归还连接
MyDataPool.closeAll(null, null, conn);
System.out.println("==========================");
Connection conn1 = MyDataPool.getConnFromPool();
String sql = "select * from user where uid = ?";
PreparedStatement psvm = conn1.prepareStatement(sql);
//设置参数
psvm.setInt(1, 1);
//执行
ResultSet rs = psvm.executeQuery();
while(rs.next()) {
int uid = rs.getInt("uid");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.println(uid + "\t" + username + "\t" + password);
}
//归还
MyDataPool.closeAll(rs, psvm, conn1);
}
}
DBCP连接池
DBCP也是一个开源的连接池,直接进行使用步骤介绍
- 导包
commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
- 编写数据库连接的配置文件
配置文件名称:*.properties
配置文件位置:建议放在src下
需求:测试连接池查询商品的名字。
配置文件dbcpconfig.properties
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day09_jdbc
username=root
password=123456
#<!-- 初始化连接 -->
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
方式一:
public class DBCPDemo {
public static void main(String[] args) throws Exception {
//创建properties文件对象
Properties properties = new Properties();
//通过流的方式获取properties文件
FileReader reader = new FileReader("src/dbcpconfig.properties");
//加载properties文件
properties.load(reader);
//创建数据源
DataSource datasource = BasicDataSourceFactory.createDataSource(properties);
// System.out.println(datasource);
//通过数据源获取连接
Connection conn = datasource.getConnection();
//执行sql进行预编译
PreparedStatement psvm = conn.prepareStatement("select * from product");
//执行
ResultSet rs = psvm.executeQuery();
while(rs.next()) {
int pid = rs.getInt("pid");
String pname = rs.getString("pname");
double price = rs.getDouble("price");
System.out.println(pid+pname+price);
}
//释放资源
rs.close();
psvm.close();
conn.close();
}
}
方式二:
public class DBCPDemo2 {
public static void main(String[] args) throws Exception {
//创建properties文件对象
Properties properties = new Properties();
//通过流的方式获取properties文件
// DBCPDemo2.class.getClassLoader() 获取当前类的类加载
InputStream inputStream = DBCPDemo2.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
//加载properties文件
properties.load(inputStream);
//创建数据源 数据源工厂 创建数据源 从properties里面获取
DataSource datasource = BasicDataSourceFactory.createDataSource(properties);
// System.out.println(datasource);
//通过数据源获取连接
Connection conn = datasource.getConnection();
//执行sql进行预编译
PreparedStatement psvm = conn.prepareStatement("select * from product");
//执行
ResultSet rs = psvm.executeQuery();
while(rs.next()) {
int pid = rs.getInt("pid");
String pname = rs.getString("pname");
double price = rs.getDouble("price");
System.out.println(pid+pname+price);
}
//释放资源
rs.close();
psvm.close();
conn.close();
}
}
C3P0连接池
- 导包
c3p0-0.9.1.2和mchange-commons-java-0.2.3.4
- 倒入配置文件,放在src下(在使用c3p0连接获取数据源时,会自动读取配置文件,无需读取操作)
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://127.0.0.1:3306/day09_jdbc</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出
SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
<property name="checkoutTimeout">30000</property>
<!--隔多少秒检查连接池的空闲连接,0表示不检查-->
<property name="idleConnectionTestPeriod">30</property>
<!-- 初始化连接数 -->
<property name="initialPoolSize">10</property>
<!-- 连接的最大空闲时间,默认为0秒、不会关闭任何连接。设置30秒,30秒到期后,
连接若未使用就会被关闭 -->
<property name="maxIdleTime">30</property>
<!-- 池中最多的连接存放数目 -->
<property name="maxPoolSize">100</property>
<!-- 池中最少的连接存放数目 -->
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
<user-overrides user="test-user">
<property name="maxPoolSize">10</property>
<property name="minPoolSize">1</property>
<property name="maxStatements">0</property>
</user-overrides>
</default-config>
<!-- 命名的配置 -->
<named-config name="offcn">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/day09_jdbc</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">0</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
测试类
public class DemoC3p0 {
public static void main(String[] args) throws Exception {
//创建数据源对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//获取连接
Connection conn = dataSource.getConnection();
//System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@224aed64
//执行sql进行预编译 不用createStatement是为了解决sql注入问题
PreparedStatement psvm = conn.prepareStatement("select * from product");
//执行
ResultSet rs = psvm.executeQuery();
while(rs.next()) {
int pid = rs.getInt("pid");
String pname = rs.getString("pname");
double price = rs.getDouble("price");
System.out.println(pid+pname+price);
}
}
}
也可以对其进行封装
public class C3p0Utils {
//创建数据源对象
private static DataSource dataSource = new ComboPooledDataSource();
//创建连接池 为了DBUtils工具提前准备
public static DataSource getDataSource() {
return dataSource;
}
//获取连接
public static Connection getConnection() {
try {
Connection conn = dataSource.getConnection();
return conn;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Druid连接池
- 导包 druid-1.0.9
- 倒入配置文件 放在src下面
druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=123456
# 初始化连接数
initialSize=5
#最大连接数
maxActive=10
#超时时间
maxWait=3000
- 写一个工具类对配置文件进行读取操作
public class DruidUtils {
private static DataSource dataSource;
//在静态代码块里面完成对数据源的初始化
static {
//拿到数据源对象
Properties properties = new Properties();
//获取配置文件
try {
FileInputStream inputStream = new FileInputStream("src/druid.properties");
//加载通过数据源进去
properties.load(inputStream);
//druid下面的方法
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取数据源方法
public static DataSource getDataSource() {
return dataSource;
}
}
- 测试
第一种:手动
public class TestDruid {
public static void main(String[] args) throws Exception {
/**
* 有了数据源之后
* 要么手动获取连接 就是 调用getConnection()的方法获取
* 要么就是创建QueryRunner时指定获取连接
*/
//增加
DataSource dataSource = DRUIDUtils.getDataSource();
Connection connection = dataSource.getConnection();
QueryRunner queryRunner = new QueryRunner();
int row = queryRunner.update(connection,"insert into user values(null,?,?,?)","都是","145","男");
System.out.println(row>0?"添加成功":"添加失败");
//释放资源
//因为不是手动创建的,而是调用的连接池里面的,所以释放资源是放回连接池当中,而不是销毁
org.apache.commons.dbutils.DbUtils.close(connection);
}
}
第二种:自动
public class TestDruid {
public static void main(String[] args) throws Exception {
/**
* 有了数据源之后
* 要么手动获取连接 就是 调用getConnection()的方法获取
* 要么就是创建QueryRunner时指定获取连接
*/
//第二种自动的
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "select * from product where pid = ?";
Product product = qr.query(sql, new BeanHandler<Product>(Product.class),2);
System.out.println(product);
}
}
总结:
使用Properties
获取文件对象的时候
DBCP和Druid都需要读取配置文件,C3P0会自动读取配置文件(前提是放在src下),获取的时候用get方法获取就好
用QueryRunner
方法获取对象的时候
Druid,c3p0,DBCP都可创建实体类进行操作
DBCP连接池与C3P0连接池的区别:
- DBCP连接池使用效率高, 而C3P0效率相比较偏低;
- C3P0安全性较高, 而DBCP安全性偏低, 容易丢失连接;
需要的jar包
链接:https://pan.baidu.com/s/1_nqg99Fb83FA1KaFXsip_g
提取码:1314
复制这段内容后打开百度网盘手机App,操作更方便哦