1 連接池概述
- 連接對象的使用問題:
我們原先連接數據庫都是用一次創建連接一次,不用的話就直接關閉了,然後在使用在創建,這樣就造成了佔用資源過多加重服務器負擔,耗時。 - 解決:
解決這些問題,我們要考慮如何提高連接速度,還有就是如何提高使用率。
下面會詳細介紹一種最實用的數據庫連接池的詳細介紹及其使用------德魯伊(druid)
經過上面的介紹,我們需要創建一個連接池,以供我們連接數據庫操作,在沒用連接池之前,用戶訪問數據庫的時候都是自己創建連接對象的。
我們使用了連接池之後:從系統開始啓動的時候就會創建一個工廠對象,裏面有一定數量的數據庫連接對象,用戶使用的時候直接會從池子裏拿連接對象,不需要自己在創建了。
連接池解決現狀問題的原理
Connection連接對象 | 操作特點 |
---|---|
創建時 | 連接對象不再由自己創建,而是系統啓動的時候已經創建一定數量的連接, 並且放在連接池中 |
使用時 | 直接從連接池中去獲取一個已經創建好的連接對象即可 |
關閉時 | 不是真的關閉連接對象,而是將連接對象再放回到連接池中,供下一個用戶使用 |
- 數據源接口:javax.sql.DataSource接口
數據源接口中的方法:
DataSource接口中的方法 | 描述 |
---|---|
Connection getConnection() | 從連接池中獲取連接對象 |
每個連接池都會有很多的參數,每個參數都有不同的含義,幾乎所有的參數都是由默認值的,參數名在不同的連接池中代表的意思也有所差異!
常用參數 | 描述 |
---|---|
初始連接數 | 服務器啓動的時候創建的連接對象數量 |
最大連接數 | 連接池中最多可以允許放多少個連接對象 |
最長等待時間 | 如果連接池中沒有連接對象,設置用戶等待的最長時間是多久,單位是毫秒。 如果超過這個時間就拋出異常 |
最長空閒回收時間 | 如果一個連接對象長時間沒有人使用,設置多久回收這個對象,默認是不回收。 |
2 常用的連接池(我們首選druid)
常用連接池的介紹
DataSource本身是Oracle公司提供的一個接口,本身沒有具體的實現,它的實現由各大連接池的數據庫廠商去實現,我們只需要學習如何使用就ok了。
常用的連接池組件:
- 阿里巴巴-德魯伊druid連接池:Druid是阿里巴巴開源平臺上的一個項目(主要)。
- DBCP(DataBase Connection Pool)數據庫連接池,是Apache上的一個Java連接池項目,也是Tomcat使用的連接池組件。
- C3P0是一個開源的JDBC連接池,目前使用它的開源項目有Hibernate,Spring等。C3P0有自動回收空閒連接功能。
使用Druid連接池
DRUID簡介
Druid是阿里巴巴開發的號稱爲監控而生的數據庫連接池,在功能、性能、擴展性方面,都超過其他數據庫連接池。Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。如:一年一度的雙十一活動,每年春運的搶火車票。
Druid的下載地址:https://github.com/alibaba/druid
DRUID連接池使用的jar包:druid-1.0.9.jar
常用的配置參數
參數 | 說明 |
---|---|
url | 連接字符串 |
username | 用戶名 |
password | 密碼 |
driverClassName | 驅動類名,會自動根據URL識別,這一項可以不配置 |
initialSize | 初始連接數 |
maxActive | 最大連接數 |
maxWait | 最長等待時間 |
Druid連接池API介紹
- 得到配置文件的輸入流
Class類中的方法 | 說明 |
---|---|
InputStream getResourceAsStream(String path) | 加載類路徑下配置文件,轉成一個輸入流對象 |
- Properties類的方法,讀取屬性文件中的鍵和值,並且加載到集合中
- 通過Druid工廠的靜態方法創建連接池,提供屬性集合作爲參數
DruidDataSourceFactory的方法 | 方法 |
---|---|
public static DataSource createDataSource(Properties properties) | 通過屬性集合中屬性,創建一個連接池 |
3 使用Druid連接池
案例演示:獲取連接對象
導包:
步驟
- 在src目錄下創建一個properties文件,文件名隨意,設置上面的參數
- Java代碼
- 加載properties文件的內容到Properties對象中
- 使用工廠類,創建DRUID連接池,使用配置文件中的參數
- 從DRUID連接池中取出10個連接輸出
.properties配置文件:
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000
java代碼:
public class Demo2Druid {
public static void main(String[] args) throws Exception {
//1.從類路徑下加載配置文件,獲取一個輸入流。如果不指定路徑,默認是讀取同一個包下資源文件
InputStream inputStream = Demo2Druid.class.getResourceAsStream("/druid.properties");
//2.使用Properties對象的方法將配置文件中屬性加載到Properties對象中
Properties properties = new Properties();
//加載了配置文件中所有的屬性
properties.load(inputStream);
//3.通過druid的工廠類創建連接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//獲取10個連接對象
for (int i = 1; i <= 11; i++) {
Connection connection = dataSource.getConnection();
System.out.println("第" + i + "個連接對象:" + connection);
//第3個連接關閉
if (i==3) {
connection.close();
}
}
}
}
但是如果超過了數據庫最大連接數量:
但是我們讓第三個關閉了連接,相當於還給連接池一個連接對象,所以會打印是以個結果:(有兩個地址值是相同的!)
4 數據工具類再增強
分析
使用Druid連接池來獲取連接對象,達到提升訪問數據庫速度目的
- 去掉類中與數據庫連接有關的代碼
- 得到數據庫的連接,從連接池中獲取
- 新加一個方法,獲取連接池對象
- 在類一開始加載就能夠獲取連接池(數據源)對象,在靜態代碼塊中創建連接池
代碼
package com.aoshen.Test;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class JDBCUtils {
//聲明連接池對象
private static DataSource dataSource;
//使用靜態,是類加載的時候就創建連接池
static{
try {
//讀取配置文件
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("druid");
//獲取Properties對象,加載到該對象中
Properties properties = new Properties();
//獲取配置文件
properties.load(inputStream);
//創建druid工廠
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//獲取數據庫連接
public static Connection getConn() throws SQLException {
return dataSource.getConnection();
}
/**
* 關閉連接
* 查詢調用這個方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 關閉連接
* 增刪改沒有結果集
*/
public static void close(Connection connection, Statement statement) {
//直接調用上面的方法
close(connection, statement, null);
}
/**
* 通用的增刪改方法
*/
public static int update(String sql,Object...args){
Connection conn = null;
PreparedStatement ps = null;
//返回影響的行數
int row = 0;
try{
//獲取連接
conn = getConn();
//獲取預編譯對象
ps = conn.prepareStatement(sql);
//獲取元數據,得到有多少佔位符
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
//循環獲取賦值
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
//執行SQL語句
row = ps.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
close(conn,ps);
}
return row;
}
/**
* 通用的查詢方法
*/
public static <T> List<T> equery(String sql,Class<T>c,Object...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
//創建集合用於接收數據庫中查的值
List<T>list = new ArrayList<>();
try{
//獲取連接
conn = getConn();
//獲取預編譯對象
ps = conn.prepareStatement(sql);
//通過獲取元數據給佔位符賦值
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount();
for (int i = 0; i < count; i++) {
ps.setObject(i+1,args[i]);
}
//執行sql
rs = ps.executeQuery();
//遍歷集合,封裝到集合中嗎,一行數據封裝一個對象
while (rs.next()){
//每條記錄封裝成一個對象
T t = c.newInstance();
//得到實體類中有哪些列名
Field[] fields = c.getDeclaredFields();
//遍歷賦值
for (Field field : fields) {
//獲取列名
String name = field.getName();
//獲取內容
Object value = rs.getObject(name);
//因爲是私有的,要暴力反射
field.setAccessible(true);
//把最後得到的值賦值給創建的對象中
field.set(t,value);
}
//把最後含每一行值的對象添加到集合中
list.add(t);
}
}catch (Exception e){
e.printStackTrace();
}finally {
close(conn,ps,rs);
}
return list;
}
}
使用工具類
/**
* 使用工具類
*/
public class Demo3UseUtils {
public static void main(String[] args) {
//使用工具類添加1條記錄
int row = JdbcUtils.update("insert into student values(null,?,?,?)", "嫦娥", 0, "1997-07-07");
System.out.println("添加了" + row + "條");
//使用工具類查詢所有的數據
List<Student> students = JdbcUtils.query("select * from student", Student.class);
//打印
students.forEach(System.out::println);
}
}
小結
修改了連接的獲取方式
- 在靜態代碼塊中創建連接池對象
- 添加了獲取數據源的方法
- 修改了獲取連接的方法,從連接池中獲取連接對象