数据库连接池和线程池类似,主要是,Connection 相当浪费时间和内存(比新建线程慢多了)。
一、连接池的背景
1. jdbc的背景
(1)成立背景:每个数据库厂商都有自己的数据库驱动,为了实现我们可以方便地连接到到不同数据库,(比如你的账户信息在MySQL数据库,头像图片在Oracle数据库),
(2)jdbc定义:jdbc是一个接口规范,数据库驱动(Driver)由数据库厂商自己来完成。关系如图所示。
(3)回顾连接的步骤:启动Driver,连接获得connection,获取statement,使用statement,增删改,或者查询(获得resultSet)
2. jdbc链接池的概念:
**类似线程池:**一个容器,程序加载就自动生成几个Connection ,然后用的时候比较方便,而且用完了不是关闭Connection,而是归还。
3.为什么要使用连接池?
(1)连接数据库的Connection十分浪费资源,(比如获得connection连接需要 900 毫秒,获得statement只需30毫秒,获得 ResultSet只需要 3毫秒,)
(2)数据池技术:准备一些一些预先的资源,过来就连接准备好的预先的资源。
(3)增加同时执行SQL的数量:,你最高的statement总数为 (最高负载):Connection数 * 每个Connection产生的statement数
二、连接池的实现(不用框架)
1.myJdbcPool.java实现步骤
1.在static批量生产配置的Connection放到Victor里面(Victor线程安全)
2.在getConnection()方法里,
- 1.在集合victors取出一个Connection,
- 2.并使用动态代理技术修改close()方法,让其变成归还connection对象到集合里。
2.myJdbcPool.java 代码
import javax.sql.DataSource;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Logger;
/**
* JdbcPool 数据池(数据源)
* #### 一.myJdbcPool.java实现步骤
* 1.在loadConfiguration()批量生产配置的Connection放到Victor里面(Victor线程安全)
* 2.在getConnection()方法里,
* * (1).在集合victors取出一个Connection,
* * (2).并使用[动态代理技术]()修改close()方法,让其变成归还connection对象到集合里。
*/
public class JdbcPool implements DataSource {
public static Vector<Connection> victors = new Vector<>();
/**
* 1.在loadConfiguration()批量生产配置的Connection
* 放到Victor里面(Victor线程安全)
*/
private static void loadConfiguration() {
try {
String configPath = "jdbcPoolConf.properties";
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(configPath));
Properties properties = new Properties();
//将输入流的数据载入到Properties对象里,inputSteam都可以?
properties.load(bufferedInputStream);
//用properties对象获得配置文件的信息
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
int jdbcPoolInitSize = Integer.parseInt(properties.getProperty("jdbcPoolInitSize"));
//驱动
Class.forName(properties.getProperty("driver"));
//如果没有初始化就初始化,
if (victors.size() == 0){
for (int i = 0; i < jdbcPoolInitSize; i++) {
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println("日志--获取到链接:"+configPath);
victors.addElement(connection);
}
}
} catch (Exception e) {
System.out.println("日志--创建失败!");
e.printStackTrace();
}
}
/**
* * 2.在getConnection()方法里,
* (1).在集合victors取出一个Connection,
* (2).并使用[动态代理技术]()修改close()方法,
* 让其变成归还connection对象到集合里。
* @return Connection,close()被动态代理修改了
*
*/
@Override
public Connection getConnection() {
if (victors.size() == 0){
JdbcPool.loadConfiguration();
}
if (victors.size() > 0){
//取出一个 Connection,并给final?
final Connection connection = victors.remove(0);
//关键的动态代理技术,实现修改connection的close方法
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(!method.getName().equals("close")){
return method.invoke(connection, args);
}else{
//如果调用的是Connection对象的close方法,就把conn还给数据库连接池
victors.addElement(connection);
System.out.println(connection + "被还给Connections数据库连接池了!!");
System.out.println("Connections数据库连接池大小为" + victors.size());
return null;
}
}
});
}else {
System.out.println("日志--链接超时!");
return null;
}
}
public static void release(Connection connection, Statement statement,ResultSet resultSet){
//先开后关
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//===========================实现的方法========================
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@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> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
3.配置文件
配置文件:jdbcPoolConf.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bmft_blog?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
user=root
password=123456
jdbcPoolInitSize=10
4.效果
Run:(每个人数据库不一样,测试类请自己书写一个,这里测试类就不展示了)
三、常用的开源数据库连接池
(这里列一个纲领,具体的搜索我的博客)
1,DBCP
并发量低下,性能不要适用于小型系统,Tomcat内置
2.C3P0
单线程,性能差,适用于小型系统
3.Druid(阿里巴巴)
最好用的数据库连接池,高性能,高并发,高拓展,分析监控设计,快速交互查询,高可用(遇到一些情况,冗机还能运行)
四、如何使用Tomcat配置数据连接池
(这里列一个纲领,具体的搜索我的博客)