在實際項目開發中訪問操作數據庫,肯定要使用到連接池技術,不僅可以提升數據庫操作效率,也在某種程度上提高了代碼質量。前面我們簡單介紹了 c3p0 連接池的使用,今天我們還是以操作 mysql 為例簡單介紹一下 druid 數據庫連接池技術的使用。
druid 是阿里技術團隊提供的一款優秀的開源數據庫連接池,在國內使用非常廣泛。作為后起之秀,其功能靈活強大穩定,性能也比 c3p0 要高,大有替代其它第三方數據庫連接池的趨勢。我們也推薦在項目和產品開發中優先考慮 druid 連接池技術的使用。
druid 的 GitHub 地址為:https://github.com/alibaba/druid (GitHub 訪問可能比較慢)
druid 的 Gitee 地址為:https://gitee.com/wangmt2000/druid
一、配置 druid 連接池
我們首先創建一個 JavaSE 的 Maven 項目,在 pom.xml 文件中導入 druid 和 mysql 的 jar 包。
我們可以從 https://mvnrepository.com 上進行查找有關各種 jar 包依賴的 xml 配置內容,復制粘貼到 pom.xml 即可。
druid 的 jar 包網址是:https://mvnrepository.com/artifact/com.alibaba/druid
mysql 連接 java 的驅動 jar 包網址是:https://mvnrepository.com/artifact/mysql/mysql-connector-java
我在 pom.xml 配置的都是當前最新版本的 jar 包,如下所示:
<!-- 導入 druid 的 jar 包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- 導入 mysql 的 jar 包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
配置好引用的 jar 包后,打開右側的 Maven 窗口,刷新一下,這樣 Maven 會自動下載所需的 jar 包文件。
然后在項目的 resources 資源目錄下創建 druid 的 properties 配置文件,properties 配置文件可以隨意命名,但是文件內容中每項配置的 key 必須是指定的名稱,這里我使用 druid.properties 作為配置文件的名稱,其文件內容如下所示:
# 數據庫連接參數
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb
username=root
password=123456
# 初始化連接的數量
initialSize=3
# 最大連接的數量
maxActive=20
# 獲取連接的最大等待時間(毫秒)
maxWait=3000
當然可選配置項有很多,上面列出的只是最常用的核心配置項,有關詳細的配置項列表,可以參看以下網址:
https://github.com/alibaba/druid/wiki/DruidDataSource配置 (GitHub 訪問可能比較慢)
二、使用 druid 連接池
使用 druid 連接池,主要是使用 DruidDataSourceFactory 根據 properties 配置文件內容創建出 DataSource 數據源對象,然后調用其 getConnection 方法獲取數據庫連接對象,拿到連接對象之后,其它的操作跟 JDBC 訪問數據庫的操作一模一樣,唯一的區別就是當調用連接的 close 方法時,底層不再是關閉銷毀連接對象,而是將連接對象放入到連接池中,以便后續新的請求到來時,直接拿去使用。具體代碼如下:
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
public class druidtest {
public static void main(String[] args) throws Exception {
//加載配置文件
InputStream is = druidtest.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(is);
//根據配置文件內容,創建出數據源對象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//通過數據源對象獲取數據庫連接
//如果連接池中的連接已經被用完,則會等待一定的時間(所配置的時間)
//如果等待超時,就會拋出異常
Connection con = dataSource.getConnection();
//執行 sql 語句,獲取並打印結果集
String sql = "select e_id,e_name,e_age from employee";
PreparedStatement pst = con.prepareStatement(sql);
ResultSet rs = pst.executeQuery();
while(rs.next()) {
System.out.println(
rs.getInt("e_id") + "\t" +
rs.getString("e_name") + "\t" +
rs.getInt("e_age"));
}
//釋放資源
rs.close();
pst.close();
//這里的關閉連接,並沒有關閉和銷毀連接
//而是把連接對象,放入到連接池中,供后續訪問時直接拿去使用
con.close();
}
}
如果你之前基於 JDBC 編寫過通用的數據庫訪問類,那么將它改造為基於 druid 的數據庫訪問類,也是很容易的。
這里我提供一個相對比較完整的基於 druid 連接池的數據庫訪問類(具體細節就不詳細介紹了),內容如下所示:
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class jdbcHelper {
private static DataSource dataSource;
//靜態初始化,加載 properties 配置文件,創建數據源對象
static {
try {
InputStream is =
druidtest.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(is);
dataSource = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception ex) {
ex.printStackTrace();
}
}
//通過數據源對象,獲取連接對象
public static Connection getConnection() {
Connection con = null;
try {
con = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//釋放查詢操作相關的資源(結果集對象,SQL語句對象,歸還數據庫連接)
public static void close(Connection con, Statement stat, ResultSet rs) {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//釋放增刪改相關操作的資源(SQL語句對象,歸還數據庫連接)
public static void close(Connection con, Statement stat) {
close(con, stat, null);
}
//執行查詢 SQL 語句,返回查詢的單個值
public static <T> T queryForValue(String sql, Class<T> returnType, Object... parameters) {
T result = null;
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
con = dataSource.getConnection();
pst = con.prepareStatement(sql);
if (parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
pst.setObject(i + 1, parameters[i]);
}
}
rs = pst.executeQuery();
if (rs.next()) {
result = returnType.cast(rs.getObject(1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(con, pst, rs);
}
return result;
}
//執行查詢 SQL 語句,返回查詢的單個實體對象
public static <T> T queryForObject(String sql, Class<T> returnType, Object... parameters) {
T result = null;
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
result = returnType.getConstructor().newInstance();
con = dataSource.getConnection();
pst = con.prepareStatement(sql);
if (parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
pst.setObject(i + 1, parameters[i]);
}
}
rs = pst.executeQuery();
if (rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
for (int i = 1; i <= count; i++) {
String columnName = metaData.getColumnName(i);
Object value = rs.getObject(columnName);
PropertyDescriptor pd =
new PropertyDescriptor(columnName.toLowerCase(), returnType);
Method writeMethod = pd.getWriteMethod();
writeMethod.invoke(result, value);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(con, pst, rs);
}
return result;
}
//執行查詢 SQL 語句,返回查詢的實體對象列表
public static <T> List<T> queryForList(String sql, Class<T> returnType, Object... parameters) {
List<T> list = new ArrayList<>();
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
try {
con = dataSource.getConnection();
pst = con.prepareStatement(sql);
if (parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
pst.setObject(i + 1, parameters[i]);
}
}
rs = pst.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
while (rs.next()) {
T bean = returnType.getConstructor().newInstance();
for (int i = 1; i <= count; i++) {
String columnName = metaData.getColumnName(i);
Object value = rs.getObject(columnName);
PropertyDescriptor pd =
new PropertyDescriptor(columnName.toLowerCase(), returnType);
Method writeMethod = pd.getWriteMethod();
writeMethod.invoke(bean, value);
}
list.add(bean);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(con, pst, rs);
}
return list;
}
//執行增刪改 SQL 語句,返回受影響的行數
public static int executeSql(String sql,Object...parameters) {
int count = 0;
Connection con = null;
PreparedStatement pst = null;
try{
con = dataSource.getConnection();
pst = con.prepareStatement(sql);
if (parameters.length > 0) {
for (int i = 0; i < parameters.length; i++) {
pst.setObject(i + 1, parameters[i]);
}
}
count = pst.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
close(con,pst);
}
return count;
}
}
然后在 Maven 項目的 test 目錄下創建測試類,使用這個 druid 數據庫訪問類測試其使用方法:
import org.junit.Test;
import java.util.List;
public class jdbcHelperTest {
@Test
public void queryForValue() {
//查詢員工數量
String sql = "select count(*) from employee";
Long value = jdbcHelper.queryForValue(sql,Long.class);
System.out.println(value);
}
@Test
public void queryForList() {
//查詢所有員工
String sql = "select * from employee";
List<Employee> list = jdbcHelper.queryForList(sql, Employee.class);
for(Employee emp : list) {
System.out.println(emp);
}
}
@Test
public void queryForObject() {
//查詢一條員工,並打印出來
String sql = "select * from employee WHERE e_id=?";
Employee emp = jdbcHelper.queryForObject(sql,Employee.class,1);
System.out.println(emp);
}
@Test
public void insert() {
//添加一條新員工
String sql = "insert into employee(e_id,e_name,e_age) values(?,?,?)";
Object[] params = {6,"任天蓬",40};
int result = jdbcHelper.executeSql(sql, params);
System.out.println(result);
}
@Test
public void update() {
//通過員工id,修改員工的年齡
String sql = "update employee set e_age=? where e_id=?";
Object[] params = {38,6};
int result = jdbcHelper.executeSql(sql, params);
System.out.println(result);
}
@Test
public void delete() {
//通過員工姓名,刪除員工
String sql = "DELETE FROM employee WHERE e_name=?";
int result = jdbcHelper.executeSql(sql, "任天蓬");
System.out.println(result);
}
}
OK,以上只是簡單的 druid 連接池技術的介紹,總體來說使用起來很簡單,希望對大家有用。