代码实现数据库连接池

数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。

大家可以想一下,如果项目中没有数据库连接池会是一个什么样的结果?每访问一次数据库都会新建一个数据库连接,如果同事有成百上千的请求需要访问数据库那么项目会惨不忍睹。

数据库连接池就是在项目启动是就已经创建了一定数量的数据库连接,有请求访问数据库时,不需要新建数据库连接,直接在连接池中拿就可以,用完了记得放到池子里面就行。这样的话,连接池中的数据库连接会不断的重复利用,大大提高了对数据库操作的性能。

数据库连接池的运行机制有以下几点:

1.程序启动时创建数据库连接池。

2.使用连接时,从连接池中拿可用的数据库连接。

3.使用完,将数据库连接放入数据库中。

那么要想手动代码实现数据库连接池,需要哪些步骤了?下面会贴出源代码以及简单的注释。

首先需要一个数据库连接池的配置文件jdbc.properties,内容如下:

#驱动名称
jdbcDriver=com.mysql.jdbc.Driver
#连接URL
jdbcUrl=jdbc:mysql://localhost:3306/lcma
#数据库账户名
userName=root
#数据库密码
password=iflytek
#初始化连接数
initCount=10
#步进数量
stepSize=4
#最大连接池个数
poolMaxSize=150

既然是数据连接池,那么池子里面放的肯定是很多个数据库连接,一个数据库连接就是一个对象,建一个数据库连接类 PooledConnection.java代表数据库连接对象。类里面有个isBusy属性,用来表示该连接对象是否正在使用,若使用中用true表示,反之为false。代码如下:

package com.lcma.conn;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * <p>Title      : 存储连接池中对象属性</p>
 * <p>Description: [子应用名]_[模块名]</p>
 * <p>Copyright  : Copyright (c) 2016</p>
 * <p>Company    : 科大讯飞</p>
 * @author       : lcma
 * @version      : 1.0
 */
public class PooledConnection {
	
	/**
	 * 连接管道对象
	 */
	private Connection conn;
	
	/**
	 * 连接状态,true-繁忙,false-空闲
	 */
	private boolean isBusy = false;
	
	public PooledConnection(Connection conn, boolean isBusy){
		this.conn = conn;
		this.isBusy = isBusy;
	}
	
	public void close(){
		this.isBusy = false;
	}

	public Connection getConn() {
		return conn;
	}

	public void setConn(Connection conn) {
		this.conn = conn;
	}

	public boolean isBusy() {
		return isBusy;
	}

	public void setBusy(boolean isBusy) {
		this.isBusy = isBusy;
	}

	/**
	 * <p>Discription:创建查询方法,用于测试使用</p>
	 * @param sql
	 * @return
	 * @author       : lcma
	 * @update       : 2016年12月5日下午11:19:18
	 */
	public ResultSet queryBySql(String sql){
		ResultSet rs = null;
		Statement sm = null;
		try {
			sm = conn.createStatement();
			rs = sm.executeQuery(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
	
	
}

创建连接池接口类,IMyPool.java

package com.lcma.pools;

import com.lcma.conn.PooledConnection;

/**
 * <p>Description: 连接池接口</p>
 * <p>Copyright  : Copyright (c) 2017</p>
 * @author       : lcma
 * @version      : 1.0
 */
public interface IMyPool {
	
	/**
	 * <p>Discription:创建连接</p>
	 * @param count
	 * @author       : lcma
	 * @update       : 2016年12月4日下午1:11:46
	 */
	public void createConnection(int count);
	
	/**
	 * <p>Discription:获取连接</p>
	 * @return
	 * @author       : lcma
	 * @update       : 2016年12月4日下午1:11:34
	 */
	public PooledConnection getConnection();

}

实现连接池接口类,MyPoolImpl.java

1.读取配置文件,创建连接池

2.获取连接,判断是否有空闲的连接并且是有效的连接

package com.lcma.pools.impl;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

import com.lcma.conn.PooledConnection;
import com.lcma.pools.IMyPool;

/**
 * <p>Title      : 连接池接口实现类</p>
 * <p>Description: [子应用名]_[模块名]</p>
 * <p>Copyright  : Copyright (c) 2016</p>
 * <p>Company    : 科大讯飞</p>
 * @author       : lcma
 * @version      : 1.0
 */
public class MyPoolImpl implements IMyPool {
	/**
	 * 驱动名称
	 */
	private String jdbcDriver;
	/**
	 * 连接地址
	 */
	private String jdbcUrl;
	/**
	 * 用户名
	 */
	private String userName;
	/**
	 * 密码
	 */
	private String password;
	/**
	 * 初始化连接数
	 */
	private int initCount;
	/**
	 * 步进连接数
	 */
	private int stepSize;
	/**
	 * 最大连接数
	 */
	private int poolMaxSize;
	
	/**
	 * 连接池容器
	 */
	private static Vector<PooledConnection> pooledConnections = new Vector<PooledConnection>();
	
	/**
	 * 构造函数,执行初始化方法
	 */
	public MyPoolImpl() {
		init();
	}
	
	/**
	 * <p>Discription:初始化连接池</p>
	 * @author       : lcma
	 * @update       : 2016年12月5日下午8:42:37
	 */
	private void init(){
		InputStream is = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
		Properties pro = new Properties();
		try {
			pro.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
		jdbcDriver = pro.getProperty("jdbcDriver");
		jdbcUrl = pro.getProperty("jdbcUrl");
		userName = pro.getProperty("userName");
		password = pro.getProperty("password");
		initCount = Integer.parseInt(pro.getProperty("initCount"));
		stepSize = Integer.parseInt(pro.getProperty("stepSize"));
		poolMaxSize = Integer.parseInt(pro.getProperty("poolMaxSize"));
		
		try {
			Driver driver = (Driver)Class.forName(jdbcDriver).newInstance();
			//将driver注册
			DriverManager.registerDriver(driver);;
		} catch (Exception e) {
			e.printStackTrace();
		}
		//创建连接
		createConnection(initCount);
	}

	/**
	 * <p>Discription:创建连接</p>
	 * @param count
	 * @author       : lcma
	 * @update       : 2016年12月4日下午1:11:46
	 */
	@Override
	public void createConnection(int count) {
		if(poolMaxSize<=0 || pooledConnections.size()+count > poolMaxSize){
			System.out.println("创建连接失败,超过最大连接数");
			throw new RuntimeException("创建连接失败,超过最大连接数");
		}
		try {
			//循环创建连接
			for(int i = 0; i < count; i++){
	        	 //创建连接
				Connection connection = DriverManager.getConnection(jdbcUrl, userName, password);
			    //实例化连接池中的连接
				PooledConnection pooledConnection = new PooledConnection(connection, false);
				//存入连接池容器
				pooledConnections.add(pooledConnection);
			}
         } catch (SQLException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * <p>Discription:获取连接</p>
	 * @return
	 * @author       : lcma
	 * @update       : 2016年12月4日下午1:11:34
	 */
	@Override
	public PooledConnection getConnection() {
		if(pooledConnections.size()<=0){
			System.out.println("获取连接失败,连接池为空");
			throw new RuntimeException("获取连接失败,连接池为空");
		}
		PooledConnection connection = getRealConnection();
		//判断是否为空
		while(connection == null){
			//创建connection,步进数
			createConnection(stepSize);
			//重新获取连接,有可能获取的还为空,采用while循环判断
			getRealConnection();
			//防止其他线程过来拿连接
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return connection;
	}
	
	//判断我们是否拿到有效的连接对象
	private synchronized PooledConnection getRealConnection(){
		//先判断连接池是不是有我们需要的空闲连接对象
		for(PooledConnection connection : pooledConnections){
			//未处于繁忙状态
			if(!connection.isBusy()){
				Connection conn = connection.getConn();
				try {
					//判断这个连接是不是有效,isValid就是创建了一个statement,执行sql语句,看是否成功
					if(!conn.isValid(2000)){
						Connection validConn = DriverManager.getConnection(jdbcUrl, userName, password);
						connection.setConn(validConn);
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
				//设置为繁忙
				connection.setBusy(true);
				return connection;
			}
		}
		return null;
	}

	public String getJdbcDriver() {
		return jdbcDriver;
	}

	public void setJdbcDriver(String jdbcDriver) {
		this.jdbcDriver = jdbcDriver;
	}

	public String getJdbcUrl() {
		return jdbcUrl;
	}

	public void setJdbcUrl(String jdbcUrl) {
		this.jdbcUrl = jdbcUrl;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getInitCount() {
		return initCount;
	}

	public void setInitCount(int initCount) {
		this.initCount = initCount;
	}

	public int getStepSize() {
		return stepSize;
	}

	public void setStepSize(int stepSize) {
		this.stepSize = stepSize;
	}

	public int getPoolMaxSize() {
		return poolMaxSize;
	}

	public void setPoolMaxSize(int poolMaxSize) {
		this.poolMaxSize = poolMaxSize;
	}


}


创建PoolManager类,利用内部类单例模式解决多线程问题, 多个线程在加载内部类的时候线程是互斥的,解决了线程安全问题。

package com.lcma.manager;

import com.lcma.pools.impl.MyPoolImpl;

/**
 * <p>Title      : 利用内部类单例模式解决多线程问题</p>
 * <p>Description: [子应用名]_[模块名]</p>
 * <p>Copyright  : Copyright (c) 2016</p>
 * <p>Company    : 科大讯飞</p>
 * @author       : lcma
 * @version      : 1.0
 */
public class PoolManager {
	
	private static class creatPool{
		private static MyPoolImpl poolImpl = new MyPoolImpl();
	}
	
	//多个线程在加载内部类的时候线程是互斥的,所以用单例模式的内部类形式避免线程混乱
	public static MyPoolImpl getInstace(){
		return creatPool.poolImpl;
	}

}

测试类MyPoolMain,测试2000个线程同时请求数据库,程序应对自如,这就是数据库连接池的强大之处。
package com.lcma.main;

import java.sql.ResultSet;
import java.sql.SQLException;

import com.lcma.conn.PooledConnection;
import com.lcma.manager.PoolManager;
import com.lcma.pools.impl.MyPoolImpl;

/**
 * <p>Description: 测试类</p>
 * <p>Copyright  : Copyright (c) 2017</p>
 * @author       : lcma
 * @version      : 1.0
 */
public class MyPoolMain {
	
	/**
	 * 获取连接池容器实现类
	 */
	private static MyPoolImpl poolImpl = PoolManager.getInstace();
	
	/**
	 * 单个连接查询测试
	 */
	public synchronized static void selectData(){
		PooledConnection connection = poolImpl.getConnection();
		ResultSet rs = connection.queryBySql("select * from class");
		try {
			while(rs.next()){
				System.out.print(rs.getString("ID") + "\t\t");
				System.out.print(rs.getString("NAME") + "\t\t");
				System.out.print(rs.getString("TEACHER") + "\t\t");
				System.out.println();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			try {rs.close();} catch (SQLException e) {e.printStackTrace();}
			connection.close();
		}
	}
	
	/**
	 * <p>Discription:测试2000个线程</p>
	 * @param args
	 * @author       : lcma
	 * @update       : 2016年12月5日下午11:44:18
	 */
	public static void main(String[] args){
		for (int i = 0; i < 2000; i++) {
			new Thread(new Runnable() {
				public void run() {
					selectData();
					System.out.println();
				}
			}).start();
		}
		
	}

}

注:上面代码只对功能做出实现,对代码规范性并没有做太高要求。






  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,下面是一个 Python 3.x 实现数据库连接池单例模式的例子,使用了线程安全的单例模式: ```python import sqlite3 from queue import Queue from threading import Lock class ConnectionPool: __instance = None __lock = Lock() @staticmethod def get_instance(): if ConnectionPool.__instance is None: with ConnectionPool.__lock: if ConnectionPool.__instance is None: ConnectionPool.__instance = ConnectionPool() return ConnectionPool.__instance def __init__(self, max_size=10, db_file=':memory:'): self.max_size = max_size self.db_file = db_file self.pool = Queue(maxsize=max_size) for _ in range(max_size): connection = sqlite3.connect(db_file) self.pool.put(connection) def get_connection(self): return self.pool.get() def release_connection(self, connection): self.pool.put(connection) ``` 在上面的例子中,`ConnectionPool` 是一个单例,它维护了一个 SQLite 数据库连接池。通过 `get_instance` 方法获取单例对象,通过 `get_connection` 方法从连接中获取一个数据库连接,通过 `release_connection` 方法将连接释放回连接。 在构造函数中,我们创建了一个指定大小的连接,并将连接放入队列中。在 `get_connection` 方法中,我们从队列中获取连接,如果队列为空,则会阻塞,直到有可用连接为止。在 `release_connection` 方法中,我们将连接放回队列中。 使用单例模式,可以保证我们只有一个数据库连接池实例,避免了在多个地方同时创建数据库连接池实例的问题。同时,使用线程安全的单例模式,可以保证在多线程环境下,单例对象的创建过程是线程安全的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值