数据库连接池
原数据库连接机制:
数据库连接——执行语句——执行完毕——释放资源
在“连接——释放”的过程中,是十分得浪费系统资源的。
因此,有了“池化技术”——预先准备好一些资源,等连接的时候,直接连上预先准备的。
1.举例:
1.原本从IDEA调用数据库的东西,都需要有:
con = JdbcUtils.getConnection();
来建立连接,之后再
JdbcUtils.release(con,pres,res);
释放资源。
现在直接准备好了许多个con,让你用来建立连接,完成再归还。无需在自己建立。
2.设想银行的存取款:
银行不是为了你一个人开的,如果每个客户去银行,银行都要开门一次,完成业务后,银行就关门,下次再来客户,再次开门。这是十分麻烦的。
因此,现在有一个机制:
银行准备好一些员工,开门后等待客户上门。员工等待着为客户服务,客户来了之后,直接开始办理业务。
这里员工的数目,可以类比连接池的专业名词:
常用连接数:27(通常情况下的同时办理业务的人数)
最小连接数:Eg:7(最少有7个员工在等待为客户服务)
最大连接数:Eg:37(业务承载上限)
排队等待:超过Eg:37之后的业务,需要等待前面的完成后再进行。
等待超时:Eg:100ms超过一定时间后,系统返回一个消息,让程序取消等待,告诉他们不用再等了。
2.编写连接池
通过一个接口:DataSource,来实现。
3.开源数据的实现
DBCP
(1)添加Jar包:commons-dbcp2-2.7.0.jar;commons-pool2-2.8.0.jar;
http://commons.apache.org/proper/commons-pool/download_pool.cgi
本文数据库:
CREATE DATABASE JdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE JdbcStudy;
CREATE TABLE `users`(
`id` INT PRIMARY KEY,
`NAME` VARCHAR(40),
`PASSWORD` VARCHAR(40),
`email` VARCHAR(60),
`birthday` DATE
);
INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) VALUES
(1,'ALita','123456','Alita@qq.com','2022-12-04'),
(2,'Edwin','456798','Edwin@qq.com','2000-12-04'),
(3,'Jarvis','789123','Jarvis@qq.com','2000-12-04');
select * from user;
配置文件:
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcStudy?useUnicode = true&characterEncoding = utf8&useSSL = true
username = root
password = 1234
#DBCP配置文件
#连接设置,这里的名字driverClassName,是DBCP数据源中定义好的
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy?userUnicode=true&characterEncoding=utf8&uesSSL=true
username=root
password=1234
#<!-- 初始化连接 -->
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=true
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
静态方法:
package com.Edwin.lession05.utils;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @author Edwin D
* @date 2020.5.23 上午 9:22
*/
public class JdbcUtils_DBCP {
private static BasicDataSource dataSource = null;
static {
try {
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
Properties properties = new Properties();
properties.load(in);
// 创建数据源,工厂模式:
// BasicDataSourceFactory->工厂
// createDataSource->创建对象
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 释放资源
public static void release(Connection con, Statement sta, ResultSet res) {
if (res != null) {
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (sta != null) {
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
插入数据:
package com.Edwin.lession05;
import com.Edwin.lession05.utils.JdbcUtils_DBCP;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Date;
/**
* @author Edwin D
* @date 2020.5.23 下午 5:02
*/
public class TestDBCP {
public static void main(String[] args) {
Connection con = null;
PreparedStatement pres = null;
try {
con = JdbcUtils_DBCP.getConnection();
// PreparedStatement开始于Statement的有区别:
// 使用“?”占位符来代替参数,可以提高效率,
String sql = "insert into `users` (`id`,`name`,`password`,`email`,`birthday`) values(?,?,?,?,?)";
pres = con.prepareStatement(sql);
// 此处的sql语句,属于预编译状态,写,但是不执行。
// 手动复制
pres.setInt(1,5);
pres.setString(2,"Duan");
pres.setString(3,"654321");
pres.setString(4,"Duan@qq.com");
// 注意:Date的类型有多种:
// sql.Date -> 数据库,代码java.sql.Date()转化时间。
// util.Date -> Java,代码new Date().getTime()用于获得时间戳,
pres.setDate(5,new java.sql.Date(new Date().getTime()));
// 执行
int i = pres.executeUpdate();
if (i > 0) {
System.out.println("TestDBCP insert Successful!");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtils_DBCP.release(con,pres,null);
}
}
}
输出由于某些玄学的东西,导致无法输出,并且一直在报错,无奈……等洒家解决了再写一篇。
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class com.Edwin.lession05.utils.JdbcUtils_DBCP
at com.Edwin.lession05.TestDBCP.main(TestDBCP.java:43)
参考文献
《【狂神说Java】MySQL最新教程通俗易懂》
视频连接:https://www.bilibili.com/video/BV1NJ411J79W
2020.05.24