JDBC-连接池

本文介绍了数据库连接池的概念和作用,强调了其在提升性能和减少资源消耗方面的重要性。以Druid数据库连接池为例,详细阐述了如何配置和使用Druid,包括通过配置文件初始化和手动配置连接池的方法。此外,文章还展示了如何通过封装通用数据库操作方法,提高代码复用性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.连接池

1.1 概述

池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资 源开销。

例如,数据库连接池,在系统初始化时创建一定数量数据库连接对象,需要时直接从池中取出一个空闲 对象,用完后并不直接释放掉对象,而是再放到对象池中,以便下一次对象请求可以直接复用。

这样可以消除对象创建和销毁所带来的延迟,从而提高系统的性能。

例如,连续不断的创建数据库连接对象

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.junit.jupiter.api.Test;
public class ConnectionTest {
private String driverClass = "oracle.jdbc.driver.OracleDriver";
private String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
private String user = "briup";
private String password = "briup";
@Test
public void test() {
Connection conn = null;
try {
Class.forName(driverClass);
for (int i = 1; i < 30; i++) {
conn = DriverManager.getConnection(url,user,password);
System.out.println(i+" : "+conn);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//运行结果:
1 : oracle.jdbc.driver.T4CConnection@28d6290
2 : oracle.jdbc.driver.T4CConnection@408b35bf
3 : oracle.jdbc.driver.T4CConnection@15bcf458
4 : oracle.jdbc.driver.T4CConnection@43c67247
5 : oracle.jdbc.driver.T4CConnection@726386ed
6 : oracle.jdbc.driver.T4CConnection@14bb2297
7 : oracle.jdbc.driver.T4CConnection@797501a
8 : oracle.jdbc.driver.T4CConnection@57f791c6
9 : oracle.jdbc.driver.T4CConnection@6c4f9535
10 : oracle.jdbc.driver.T4CConnection@30c31dd7
11 : oracle.jdbc.driver.T4CConnection@596df867
12 : oracle.jdbc.driver.T4CConnection@241a53ef
13 : oracle.jdbc.driver.T4CConnection@2db2cd5
14 : oracle.jdbc.driver.T4CConnection@615f972
15 : oracle.jdbc.driver.T4CConnection@31500940
16 : oracle.jdbc.driver.T4CConnection@48e64352
17 : oracle.jdbc.driver.T4CConnection@2ef8a8c3
18 : oracle.jdbc.driver.T4CConnection@63fd4873
java.sql.SQLException: Listener refused the connection with the following error:
ORA-12519, TNS:no appropriate service handler found

可以看出,在当前数据库环境中,创建出一定数据的数据库连接对象之后,就不无法在创建新对 象了。

而数据库连接池,就可以在种情况下,预先创建出一批数据库连接对象,然后对它们进行管理,并反复 使用,这样就不需要频繁的销毁和创建了,提供了资源的利用率。

我们使用的连接池是Druid, 是阿里巴巴开源的数据库连接池项目

1.2使用

javax.sql.DataSource 是Java中定义的一个数据源标准的接口,通过它获取到的数据库连接对象, 如果调用 close() 方法,不会再关闭连接,而是归还连接到连接池中。

例如,项目中 src下面,创建资源文件: druid.properties

注意:
maven 下的配置文件应该放到 resorces 下,也就是需要将druid.properties 放到 resources 目录下

文件内容是druid连接池的配置信息:

driverClassName=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:XE
username=briup
password=briup
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000

通过DruidDataSourceFactory获取到一个连接池对象,然后就可以通过这个连接池对象获得数据库的连接.

   private static DataSource dataSource = null;

    static {
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(properties);


        } catch (Exception e) {
            System.out.println("创建连接池发生异常:"+e.getMessage());
            System.exit(-1);
        }
    }
    public static Connection getConnection(boolean auto) throws SQLException {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(auto);
        return connection;
    }

也可以不使用配置文件,自己手动配置连接池:

    // 创建数据库连接池
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
	// 设置创建连接池时创建多少个连接
        dataSource.setInitialSize(5);
	// 设置最大连接数,当从连接池里面获取的连接超过了5个会继续创建,超过了10个就需要等待
        dataSource.setMaxActive(10);

2.封装

通过前面的例子,可以看出来,很多代码都是一样,特别是在执行DDL和DML语句的时候,除了SQL语句 不同之外,其他代码都是一样,那么我们就可以把这些相同的代码进行封装:

package jdbctest;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.function.Function;

public class DBUtil implements Serializable {
    private static DataSource dataSource = null;

    static {
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(properties);


        } catch (Exception e) {
            System.out.println("创建连接池发生异常:"+e.getMessage());
            System.exit(-1);
        }
    }


    public static Connection getConnection(boolean auto) throws SQLException {
        Connection connection = dataSource.getConnection();
        connection.setAutoCommit(auto);
        return connection;
    }

    public static void executeUpdate(String sql) throws SQLException {
        Connection connection = getConnection(false);
        Statement statement = connection.createStatement();
        statement.execute(sql);
        connection.commit();
        close(connection,statement,null);
    }
	//执行查询语句的时候,和DML、DDL语句不同,需要处理结果集 ResultSet ,就是通过把一个rs对
		象,处理过后的得到一个指定类型的对象,那么这里可以利用函数式编程中的 Function 接口
    public static <T> List<T> queryToList(String sql, Function<ResultSet,List<T>> function) throws SQLException {
        Connection connection = getConnection(true);
        Statement st = connection.createStatement();
        ResultSet resultSet = st.executeQuery(sql);
        List<T> list = function.apply(resultSet);
        close(connection,st,resultSet);
        return list;
    }

    public static <E> E queryOne(String sql,Function<ResultSet,E> function) throws SQLException {
        Connection connection = getConnection(true);
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        E e = function.apply(resultSet);
        close(connection,statement,resultSet);
        return e;
    }

	//
    public static  <U> List<U> queryToList(String sql,Class<U> clazz) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /*
        *  得到结果集
        * 将结果集转换成U类型的对象
        * 将结果添加到List
        * */
        Connection connection = getConnection(true);
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        List<U> list = new ArrayList<>();
        while (resultSet.next()){
            U u = clazz.getConstructor().newInstance();

            list.add(u);
        }

        return null;
    }


    public static void close(Connection conn, Statement statement, ResultSet rs){
            try {
                if (conn!=null) {
                    conn.close();
                }
                if (statement!=null)
                    statement.close();
                if (rs!=null)
                    rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
    }

}

将来使用 queryToList的时候,传一个sql语句和函数即可,这里同时也使用泛型

思考,能否封装一个方法,我们传一个指定类型,将来查询结果就自动返回这个指定的类型?

//该方法表示将查询结果封装为指定类型的对象,并存放集合中,再返回
public static <T> List<T> queryForList(String sql,Class<T> clazz) {
List<T> result = new ArrayList<>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
//解析这个传入的类型(clazz),获取到它的每一个 属性类型和属性名
List<TypeAndName> list = parse(clazz);
T obj = null;
while(rs.next()) {
//反射创建对象
obj = clazz.newInstance();
//循环拿到指定类型中的每一个 属性类型和属性名字
for(TypeAndName typeAndName : list) {
//根据属性类型,决定调用getXxx方法获取查询结果中指定字段上的值
//并将值封装到对象的属性中
if("long".equals(typeAndName.type)) {
long value = rs.getLong(typeAndName.name);
typeAndName.invokeSet(obj,long.class, value);
}
else if("String".equals(typeAndName.type)) {
String value = rs.getString(typeAndName.name);
typeAndName.invokeSet(obj,String.class, value);
}
else if("int".equals(typeAndName.type)) {
int value = rs.getInt(typeAndName.name);
typeAndName.invokeSet(obj,int.class, value);
}
//...如果需要可以继续编写其他类型和getXxx方法的对应关系
}
//将封装好的对象存入集合
result.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(rs,stmt,conn);
}
return result;
}
//解析后,返回封装好的List集合
private static <T> List<TypeAndName> parse(Class<T> clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
List<TypeAndName> list = new ArrayList<>();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
String name = field.getName();
String type = field.getType().getSimpleName();
list.add(new TypeAndName(type,name));
}
return list;
}
//私有的静态内部类,辅助解析类型(clazz)
//封装指定类型中的 属性类型和属性名
private static class TypeAndName{
String type;
String name;
public TypeAndName(String type, String name) {
this.type = type;
this.name = name;
}
//反射调用指定对象中的setXxx方法,将值存放到属性中
public <T> void invokeSet(Object obj,Class<T> c,Object value) {
try {
Method m =
obj.getClass().getDeclaredMethod("set"+initCap(name),c);
m.invoke(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
//字符串首字母大写
private String initCap(String name) {
return name.substring(0, 1).toUpperCase()+name.substring(1);
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值