数据库连接总结
一.数据库连接池的概念
1.前言:
在使用数据库连接池之前,我们是通过程序直接连接数据库或释放数据库源
但频繁的连接及释放数据库资源,会大量消耗系统资源,效率低下,而且对于频繁的连接和释放都有次数的限制
在实际使用的数据库都不在本机,而使用的都是远程数据库,再加上网络延迟,我们频繁的连接及关闭数据库势必导致效率更低
我们在开发中都会使用数据库连接池技术来实现对数据库的连接和资源释放,以提高效率
2.正文(概念):
(1)当系统运行时,连接池对自动与数据库建立一定数量(初始化数量)的连接对象,并将连接对象放到连接池中等待使用
(2)当程序需要使用连接对象操作数据库时,会在连接池中查找未使用的连接对象,找到该连接对象,便把其标记为繁忙;当程序使用完毕后,关闭与数据库的连接,指的是关闭程序与数据库连接池的连接,并将连接池中的连接对象标记为空闲状态
(3)当连接池的连接对象不足时,连接池对自动创建连接对象,存放在连接池等待使用,在连接池中会指定最大连接数量,如果达到最大连接数量,那么就不再创建连接对象了,此时程序需要连接对象时,但是连接对象都没有处于空闲状态,那么只好加入等待队列了,当然了,等待一段时间后达到了最大等待时间,那么只好返回null
(4)在空闲的时候会存在大量的连接对象,为了更加高效地利用资源,那么会释放一部分连接对象,已达到最大优化,
(5)连接池很多:如C30p连接池 DBCP连接池 tomcat自带的连接池
Druid的连接池
二.Druid连接池的使用
1.在Maven中导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
2.编写db.properties文件:
druid.driverClassName=com.mysql.cj.jdbc.Driver
druid.url=jdbc:mysql://localhost:3307/user_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
druid.username=root
druid.password=FJG258775
#设置数据库连接池相关属性
#设置连接池中初始化连接数量
druid.initialSize=20
#设置连接池中最大的连接数量(最大活动连接)
druid.maxActive=50
#设置最小空闲连接
druid.minIdle=20
#设置最大等待时间(毫秒)
druid.MaxWait=1000
3.编写程序:
(1)工具类
package com.fan.dao;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.beanutils.ConvertUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public abstract class DBUtil {
protected Connection conn=null;
protected PreparedStatement ps=null;
protected ResultSet rs=null;
private static DruidDataSource druidDataSource=null;
/**
* 连接数据库
* @return连接对象
* @throws ClassNotFoundException
* @throws SQLException
*/
protected Connection getConn1() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
return conn= DriverManager.getConnection("jdbc:mysql://localhost:3307/user_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai","root","FJG258775");
}
static {
//读取资源文件(db.properties)
//创建一个资源类实例对象
try {
Properties properties = new Properties();
//将资源文件db.properties加载内存中,生成一个输入流对象
InputStream inputStream = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
//将输入流对象添加到properties对象,此时properties对象会自动读取输入流中的数据
properties.load(inputStream);
//创建Druid数据源对象
druidDataSource = new DruidDataSource();
//将properties对象中属性自动添加到druid对象中
druidDataSource.configFromPropety(properties);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 连接池的
* @return
* @throws SQLException
*/
protected Connection getConn() throws SQLException {
//从Druid中获取连接对象
return conn=druidDataSource.getConnection();
}
protected void closeAll(){
try {
if (rs!=null){
rs.close();
}
if (ps!=null){
ps.close();
}
if (conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn=null;
ps=null;
rs=null;
}
}
protected int executeUpdate(String sql,Object...params){
//连接数据库
try {
this.getConn();
ps=conn.prepareStatement(sql);
if (params!=null&¶ms.length!=0){
for (int i=0;i<params.length;i++){
ps.setObject(i+1,params[i]);
}
}
return ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeAll();
}
return 0;
}
public <T>List<T> executeQuery(Class<T> cla,String sql,Object...params){
try {
this.getConn();
ps=conn.prepareStatement(sql);
if (params!=null&¶ms.length!=0){
for (int i=0;i< params.length;i++){
ps.setObject(i+1,params[i]);
}
}
rs=ps.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
List<T> list = new ArrayList<T>();
while(rs.next()){
T t = cla.getDeclaredConstructor().newInstance();
for (int i=0;i<columnCount;i++){
Object value = rs.getObject(i + 1);
String catalogName = metaData.getCatalogName(i + 1);
Field declaredField = cla.getDeclaredField(catalogName);
String methodName = "set" + catalogName.substring(0, 1).toUpperCase() + catalogName.substring(1);
Method declaredMethod = cla.getDeclaredMethod(methodName, declaredField.getType());
declaredMethod.invoke(t, ConvertUtils.convert(value,declaredField.getType()));
list.add(t);
}
return list;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
this.closeAll();
}
return null;
}
}
(2)测试类
package com.fan.test;
import com.fan.dao.DBUtil;
import java.sql.SQLException;
public class ConnectionTest extends DBUtil {
public static void main(String[] args) {
ConnectionTest connectionTest = new ConnectionTest();
long start = System.currentTimeMillis();
connectionTest.connTest();
long end = System.currentTimeMillis();
System.out.println("共耗时"+(end-start)+"毫秒");
}
//非连接池
public void connTest1(){
for (int i=0;i<100000;i++){
try {
super.getConn1();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
super.closeAll();
}
}
}
/**
* 连接池
*/
public void connTest(){
for (int i=0;i<10000000;i++){
try {
conn=super.getConn();
} catch (SQLException e) {
e.printStackTrace();
} finally {
super.closeAll();
}
}
}
}
(3)性能对比:
用连接池1千万的连接的耗时
共耗时2270毫秒
用传统方式1万次的耗时:
共耗时15186毫秒
为什么传统方式要用1万次了?
因为数据库的频繁连接超过一定的数量就会报异常错误
没有对比就没有伤害,从而我们可知Druid连接池是多么强大,连接池是多么未伟大的发明