数据库连接池
1. 介绍
数据库连接池是什么呢?
首先我们来回滚一下JDBC的代码流程
// 1. 注册驱动
// DriverMananger.register(new Driver());
Class.forName("com.mysql.jdbc.Driver");
// 2. 获取连接 JDBC4Connection
Connection conn = DriverManager.getConnection(String url,String username,String password);
// 3. 获取statement对象
Statement statement = conn.createStatement();
// 4. 通过statement对象发送SQL语句到MySQL服务器
ResultSet ret = statement.executeQuery("select * from account");
// 5. 解析结果集
while (ret.next()) {
String name = ret.getString("name");
int id = ret.getInt("id");
}
// 6. 关闭资源(ResultSet、Statement、Connection)
ret.close();
statement.close();
connection.close();
我们发现上述的这个JDBC最原始的流程,在一开始的时候要去和数据库之间去建立连接,然后操作数据库,操作完数据库之后就把连接等资源关闭了,而数据库的连接的资源是比较宝贵的,所以我们想要去优化我们的程序可以把这个连接反复利用起来,这样在使用JDBC的时候,一开始的时候就不需要新建连接了,而是使用之前使用过的连接,在用完了之后,也不关闭了,而是把连接保存起来,方便下一次的使用,这样做的好处可以提高效率。
那我们如何保存数据库的连接呢?我们可以利用池化的思想(线程池),把连接保存起来,这个就叫做数据库连接池
上述这种连接池的工作方式就可以做到连接的反复利用,主要的目的就是为了提高效率。
2. 手动实现一个数据库连接池
public class MyConnectionPool4 {
// 初始化大小
static int INIT_SIZE = 0;
static int MIN_SIZE = 0;
static int INCREMENT_SIZE = 0;
// 从头部存,从尾部取
static LinkedList<Connection> connectionList;
static {
// 加载配置文件
try {
FileInputStream in = new FileInputStream("pool.properties");
Properties properties = new Properties();
properties.load(in);
String initSize = properties.getProperty("init_size");
String minSize = properties.getProperty("min_size");
String increment = properties.getProperty("increment");
INIT_SIZE = Integer.valueOf(initSize);
MIN_SIZE = Integer.valueOf(minSize);
INCREMENT_SIZE = Integer.valueOf(increment);
}catch (Exception ex) {
ex.printStackTrace();
}
connectionList = new LinkedList<>();
// 初始化
addCapcity(INIT_SIZE);
}
// 获取连接
public static Connection getConnection(){
// 判断一下如果连接池里面的数量太少了,就自动扩容
if (connectionList.size() < MIN_SIZE) addCapcity(INCREMENT_SIZE);
Connection connection = connectionList.removeLast();
return connection;
}
// 返回连接
public static void returnConnection(Connection connection){
connectionList.addFirst(connection);
}
// 扩容的方法
public static void addCapcity(int size){
if (size <= 0) return;
for (int i = 0; i < size; i++) {
// 1. 获取连接
Connection connection = JDBCUtils.getConnection();
// 2. 把获取到的连接放到connectionList中
connectionList.addFirst(connection);
}
}
}
我们目前没有必要自己花大量的时间去自己手动实现一个数据库连接池,因为别人或者是别的公司已经把这个轮子造好了,我们没有必要去重复的造轮子,所以我们在数据库连接池这一块需要学会使用其他的第三方开源的数据库连接池。
3. 第三方开源的数据库连接池
在讲这三个开源的数据库连接池之前,我们说明一下其实JDBC对数据库连接池定义了一个接口,叫 javax.sql.Datasource
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}
我们发现这个数据库连接池的接口里面只定义了获取连接的方法,没有定义返回连接的方法。那么为什么没有定义返回连接的方法呢?
因为即使定义了返回连接的方法,那么一些数据库连接池的实现类中必须实现这个返回连接的方法,但是作为数据库连接池的开发者来说,即使定义了返回连接的方法,那么也没有办法阻止 数据库连接池的用户去先把连接关闭了,再去调用返回连接的方法。
3.1 DBCP
-
介绍
DBCP是Apache软件基金组织下面的一个开源项目。
Apache软件基金会:它是世界上最大的开源组织。官网, DBCP这个数据库连接池是一个比较早期数据库连接池,2010~2015的时候使用的比较多。
-
使用
- 导包
-
配置
配置dbcp.properties
#连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/36th_rev username=root password=123456 #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最大空闲连接 --> maxIdle=20 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useSSL=false;characterEncoding=utf8 #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的只读(read-only)状态。 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) defaultReadOnly= #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=REPEATABLE_READ
-
使用
public class DBCPUtils { static DataSource dataSource; static { try { Properties properties = new Properties(); properties.load(new FileInputStream("dbcp.properties")); dataSource = BasicDataSourceFactory.createDataSource(properties); }catch (Exception ex) { System.out.println("DBCP数据库连接池初始化异常..."); ex.printStackTrace(); } } // 获取连接的方法 public static Connection getConnection(){ Connection connection = null; try { connection = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } }
3.2 C3P0
-
介绍
C3p0也是一个开源的数据库连接池,是在2015~2017之间使用的用户是比较多的,这个数据库连接池相比于DBCP最大的特点是加入了连接超时自动回收的功能。
-
使用
- 导包
-
配置
C3p0的配置文件有有要求的
-
必须放在src目录下
-
必须叫做
c3p0-config.xml
xml这种文件就和html文件是一样的,文件里面由很多标签构成
xml这种文件在早期的时候是用来传输数据的(目前是json),目前大多数情况下使用xml来当做配置文件
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <!-- 在xml文件中,有一些特殊的字符是不能写的。 & > <, 对于这三个字符,我们要写转义字符 转义字符 & & > > < < >= >= <= <= --> <property name="jdbcUrl">jdbc:mysql://localhost:3306/36th_rev?useSSL=false&characterEncoding=utf8</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> </c3p0-config>
-
-
使用
public class C3p0Utils { static DataSource dataSource; static { // 这个地方为什么没有要求我们传入配置文件呢? // 因为C3p0的开发者已经帮助我们把src/c3p0-config.xml 配置文件读取了 // 所以我们在使用c3p0的时候,配置文件是有名字和位置的要求的 dataSource = new ComboPooledDataSource(); } // 获取连接 public static Connection getConnection(){ Connection connection = null; try { connection = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } }
3.3 Druid
-
介绍
Druid是国产之光,是alibaba开发的一个数据库连接池,目前alibaba已经把源代码交给了apache去维护。Druid数据库连接池最大的特点是加入了数据库连接监控的功能。
-
使用
-
导包
-
配置
我们需要配置一个properties配置文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/36th_rev?useSSL=false&characterEncoding=utf8 username=root password=123456 # 初始化大小,如果不配置,就使用默认值 initialSize=10
-
使用
public class DruidUtils { static DataSource dataSource; static { try { Properties properties = new Properties(); properties.load(new FileInputStream("druid.properties")); dataSource = DruidDataSourceFactory.createDataSource(properties); }catch (Exception ex) { System.out.println("初始化异常..."); ex.printStackTrace(); } } // 获取连接 public static Connection getConnection(){ Connection connection = null; try { connection = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return connection; } }
-