自定义数据库连接池
1、数据库连接池概述
概述
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
- 数据库连接池的几个重要的参数
- 最小连接数 :是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
- 最大连接数 :是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。
- 最小连接数与最大连接数差距 :最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。
作用
使用数据库连接池是为了资源的重复利用,在传统的向数据库获取连接时,需要用完就关闭连接,该操作会占用资源,大量用户同时获取连接关闭连接会消耗大量的资源,使用池化技术就很好的解决该问题,可以重复的利用连接。就像线程池一样,这里的数据库的连接池使用的是享元模式
的应用和动态代理
的应用。
2、自定义数据库连接池(简化版)
这里不使用配置文件,直接将连接数据库的参数写在配置文件中再加载到程序降低耦合性提高扩展性,实际的数据库连接池还需有最小连接数,最大连接数。
- 数据源
数据源必须实现DataSource,由于我们只需要重写其中的getConnection方法就采用适配器模式
package com.qiumin;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* @author qiumin
* @classname MyDataSource
* @Description love code
* @date 2022-06-05 20:07
*/
public abstract class MyDataSourceAdapter implements DataSource {
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@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;
}
}
连接池
package com.qiumin;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
/**
* @author qiumin
* @classname MyDataSource
* @Description love code
* @date 2022-06-06 16:43
*/
public class MyDataSource extends MyDataSourceAdapter{
/**
* url 最好放在配置文件中
* */
private final static String url="jdbc:mysql://localhost:3306/pool?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
/**
* userName 最好放在配置文件中
* */
private final static String userName="root";
/**
* password 最好放在配置文件中
* */
private final static String password="密码";
/**
* size 初始时连接池的大小
* */
private final static int size=5;
/**
* pools 连接池
* */
private static final LinkedList<Connection> pools = new LinkedList<>();
/**
* 加载该类时就获取连接放在连接池
* */
static {
for (int i = 0; i < size; i++) {
try {
pools.addFirst(DriverManager.getConnection(url,userName,password));
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println("数据库连接池的连接数:"+pools.size());
}
/**
* 从连接池中获取连接
* */
@Override
public Connection getConnection() throws SQLException {
final Connection conn = pools.removeLast();
System.out.println("获取完数据库连接之后的连接数:"+pools.size());
Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),
new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (name.equals("close")){ //动态代理,为close方法时就放回到连接池,不真实的关闭连接
pools.addFirst(conn);
System.out.println("返回后数据库连接之后的连接数:"+pools.size());
}else {
return method.invoke(conn,args); //其他方法正常执行
}
return null;
}
});
return proxyConn;
}
}
测试
package com.qiumin;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author qiumin
* @classname TestMyDataSourcePool
* @Description love code
* @date 2022-06-06 17:17
*/
public class TestMyDataSourcePool {
public static void main(String[] args) throws SQLException {
//数据源
MyDataSource myDataSource = new MyDataSource();
//获取连接
Connection connection = myDataSource.getConnection();
//sql
String sql = "insert into pool(id,userName,password) values (2,'qiumin','123654')";
//sql执行器对象
Statement statement = connection.createStatement();
//执行
statement.execute(sql);
statement.close();
//关闭连接,实际上是放回连接池
connection.close();
}
}
3、自定义连接池【2】
连接池,带状态
package com.qiumin;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* @author qiumin
* @classname ConnectionPool
* @Description love code
* @date 2022-06-05 21:07
*/
public class ConnectionPool {
private final static String url="jdbc:mysql://localhost:3306/pool?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
private final static String userName="root";
private final static String password="密码";
/**
*连接池大小
* */
private static int poolSize=5;
/**
* 连接池
* */
private static final Connection[] connections =new Connection[5];
/**
* 连接状态
* */
private static final AtomicIntegerArray status=new AtomicIntegerArray(new int[5]);
/**
* 构造方法
* */
static {
for (int i = 0; i < poolSize; i++) {
try {
connections[i] = DriverManager.getConnection(url,userName,password);
status.set(i,0);
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println("数据库连接池的连接数:"+connections.length);
}
/**
* 向连接池借连接
* */
public Connection borrow(){
while (true){
for (int i = 0; i < poolSize; i++) {
if(status.get(i)==0){
if (status.compareAndSet(i,0,1)){
return connections[i];
}
}
}
synchronized (this){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 归回连接到连接池
* */
public void free(Connection conn){
for (int i = 0; i < poolSize; i++) {
if (connections[1]==conn){
status.set(i,0);
synchronized (this){
this.notifyAll();
}
break;
}
}
}
}
测试
package com.qiumin;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author qiumin
* @classname TestMyDataSourcePool
* @Description love code
* @date 2022-06-06 17:17
*/
public class TestMyDataSourcePool {
public static void main(String[] args) throws SQLException {
//数据源
ConnectionPool connectionPool = new ConnectionPool();
//获取连接
Connection connection = connectionPool.borrow();
//sql执行器对象
Statement statement = connection.createStatement();
//sql
String sql = "insert into pool(id,userName,password) values (3,'qingiqng','123654')";
//执行
statement.execute(sql);
statement.close();
//关闭连接,实际上是归还连接
connectionPool.free(connection);
}
}
qiumin