數據庫連接池(connection pool)
在昨天的練習中每一次練習都需要與數據庫建立連接,完成時斷開連接,然而當處理的數據量特別的時候,就很耗費時間、降低效率,今天我們學習使用連接池,將連接放在連接池中,需要使用的時候從中取出,使用完畢放回池中並不是斷開連接。
數據庫連接池的基本思想就是爲數據庫連接建立一個“緩衝池”。預先在緩衝池中放入一定數量的連接,當需要建立數據庫連接時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。
數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量是由最小數據庫連接數來設定的。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這麼多的連接數量。連接池的最大數據庫連接數量限定了這個連接池能佔有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。
爲解決傳統開發中的數據庫連接問題,可以採用數據庫連接池技術。
數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是重新建立一個。
DBCP連接池
首先我們使用DBCP連接池(一個免費開源的連接池),我們需要先將commons-dbcp-1.4.jar文件放置當前工程下,並配置環境(添加到Build Path)。下面通過一個程序瞭解如何使用DBCP連接池: 我們在這裏和之前一樣需要創建一個“dbcp.properties”文件,將必要的參數放入其中,其內容如下,(此文件放在當前工程下),DBCP連接池使用這個文件可以完成mysql、oracle的連接池的建立,但是每次只能建立一個,另一個需要註釋起來。
driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/company
username = root
password = 123456
initialSize = 5
maxActive = 50
maxIdle = 10
#driverClassName = oracle.jdbc.driver.OracleDriver
#url = jdbc:oracle:thin:@127.0.0.1:1521:orcl
#username = scott
#password = tiger
package com.atguigu.jdbc;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;
public class DBCPTest {
@Test
public void test2() throws Exception {
Properties properties = new Properties();
properties.load(new FileInputStream("dbcp.properties"));
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
System.out.println("inital:" + ((BasicDataSource)dataSource).getInitialSize());
System.out.println("getMaxActive:" + ((BasicDataSource)dataSource).getMaxActive());
System.out.println("getMaxIdle:" + ((BasicDataSource)dataSource).getMaxIdle());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
C3P0連接池
<c3p0-config>
<named-config name="mysql-config"><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/school</property><property name="user">root</property><property name="password">123456</property><property name="acquireIncrement">5</property><property name="initialPoolSize">5</property><property name="minPoolSize">5</property><property name="maxPoolSize">50</property><property name="maxStatements">0</property><property name="maxStatementsPerConnection">5</property></named-config><named-config name="orcale-config"><property name="driverClass">oracle.jdbc.driver.OracleDriver</property><property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/school</property><property name="user">root</property><property name="password">123456</property></named-config>
</c3p0-config>
DBCP連接池使用這個文件可以完成mysql、oracle的連接池的建立,每次只能建立一個,但是另一個需要註釋起來。因爲我們是根據<named-config name="mysql-config"> 名建立連接,
package com.atguigu.jdbc;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test;
import com.mchange.v2.c3p0.*;
public class C3P0Test {
@Test
public void test1() throws PropertyVetoException, SQLException {
DataSource dataSource = new ComboPooledDataSource("mysql-config"); // 它會默認自動去讀取文件
System.out.println(dataSource);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();// 把連接歸還給連接池
DataSources.destroy(dataSource);// 完全釋放池中所有連接,並銷燬連接池!!
}
@Test
public void test2() throws PropertyVetoException, SQLException {
DataSource dataSource = new ComboPooledDataSource("oracle-config"); // 它會默認自動去讀取文件
System.out.println(dataSource);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();// 把連接歸還給連接池
DataSources.destroy(dataSource);// 完全釋放池中所有連接,並銷燬連接池!!
}
}
學習了連接池之後,JdbcUtil工具類中的getConnection方法就可以應用,如下:
package com.atguigu.jdbc;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
/**
* 預備工作 :
* 1) 把要訪問的數據庫相關的驅動程序複製到項目中, 就是jar包
* 2) 配置項目屬性, 把jar包導入到本項目的buildpath中
* @author Administrator
*
*/
public class JdbcUtil {
private static DataSource dataSource; // 聲明靜態屬性對象引用.
static {
dataSource = new ComboPooledDataSource("mysql-config"); // 連接池對象只需要創建一次就可以了
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection(); // 要想獲取連接, 只需要從連接池中獲取,用完以後, 再歸還回來
}
public static Connection getConnectionOld() throws IOException, ClassNotFoundException, SQLException {
// 1) 讀取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("jdbc.properties"));
// 2) 獲取配置文件中的必要的信息
String driverClass = properties.getProperty("driverClass");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
// 3) 註冊驅動 , 加載驅動類
Class.forName(driverClass);
// 4) 通過驅動管理器獲取連接(需要url,用戶,密碼)
return DriverManager.getConnection(url, user, password);// 暗含 new Socket(host,port), 認證,其他各種初始化操作
}
//關閉連接
public static void close(Connection connection) {
close(connection, null);
}
public static void close(Connection connection, Statement statement) {
close(connection, statement, null);
}
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//銷燬連接池
public static void destroy() {
try {
DataSources.destroy(dataSource);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
DBUtils工具類
在使用之前我們仍然需要將commons-dbutils-1.3.jar添加到當前工程下,並添加到path路徑。
package com.atguigu.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;
public class QueryRunnerTest {
// 使用我們自定義工具實現表的創建
@Test
public void test1() throws SQLException {
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
qr.update(connection, "create table test2(aa int, bb varchar(10))");
JdbcUtil.close(connection);
}
// 使用我們自定義工具向表中插入一條記錄
@Test
public void test2() throws SQLException {
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
int rows = qr.update(connection, "insert into test2(aa, bb) values(?,?)", 10, "xxx");
System.out.println(rows + " rows");
JdbcUtil.close(connection);
}
// 使用DBUtils.JDBC接口中提供的方法對departments表進行查詢,把結果集中的所有記錄轉換爲department對象集合並存入List集合中,然後遍歷輸出對象
@Test
public void test3() throws SQLException {
//query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)
String sql = "select * from departments where department_id > ?";
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
BeanListHandler<Department> rsh = new BeanListHandler<Department>(Department.class); // 把結果集中的所有記錄轉換爲對象集合
List<Department> list = qr.query(connection, sql, rsh, 20);
for (Department department : list) {
System.out.println(department);
}
}
// 使用DBUtils.JDBC接口中提供的方法對departments表進行查詢,把結果集中的一條記錄轉換爲department實體對象,然後輸出對象
@Test
public void test4() throws SQLException {
String sql = "select * from departments where department_id = ?";
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
BeanHandler<Department> rsh = new BeanHandler<Department>(Department.class); // 把結果集中的一條記錄轉換爲實體對象
Department objDepartment = qr.query(connection, sql, rsh, 20);
System.out.println(objDepartment);
}
// 使用DBUtils.JDBC接口中提供的方法對departments表進行查詢,將每一條記錄存入集合中,然後遍歷輸出每一個數據
@Test
public void test5() throws SQLException {
String sql = "select * from employees";
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
ArrayListHandler rsh = new ArrayListHandler();
List<Object[]> list = qr.query(connection, sql, rsh);
for (Object[] objects : list) {
for (int i = 0; i < objects.length; i++) {
System.out.print(objects[i] + "\t");
}
System.out.println();
}
}
// 使用DBUtils.JDBC接口中提供的方法對departments表進行查詢,將查詢到的一個數據輸出
@Test
public void test6 () throws SQLException {
String sql = "select count(*) from world.country";
QueryRunner qr = new QueryRunner();
Connection connection = JdbcUtil.getConnection();
ScalarHandler rsh = new ScalarHandler();
Object singleValue = qr.query(connection, sql, rsh);
System.out.println(singleValue);
}
@Test
public void test7() throws Exception {
QueryRunner qr = new QueryRunner();
List<Object> list = qr.query(JdbcUtil.getConnection(), "select * from student", new ColumnListHandler(1));
for (Object object : list) {
System.out.println(object);
}
}
//MapHandler把第一行數據封裝到Map集合中, 列名作爲鍵, 對應值作爲值
@Test
public void test8() throws Exception {
QueryRunner qr = new QueryRunner();
Map<String, Object> map = qr.query(JdbcUtil.getConnection(), "select * from student", new MapHandler());
Set<String> keys = map.keySet();
for (String key : keys) {
Object value = map.get(key);
System.out.println(key + " -------- " + value);
}
}
//MapListHandler把一行數據封裝到Map集合中, 並把所有行生成的Map再放入一個List集合
@Test
public void test9() throws Exception {
QueryRunner qr = new QueryRunner();
List<Map<String, Object>> list = qr.query(JdbcUtil.getConnection(), "select * from student", new MapListHandler());
for (Map<String, Object> map2 : list) {
Set<String> keys = map2.keySet();
for (String key : keys) {
Object value = map2.get(key);
System.out.println(key + " -------- " + value);
}
System.out.println();
}
}
}
到這裏就可以統一整理一下自己定義的JdbcUtil工具類、CommonUtil工具類,使自定義的工具類能達到JDButi.JDBC相同的功能,如下:package com.atguigu.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
public class JdbcUtil {
private static DataSource dataSource;
static {
dataSource = new ComboPooledDataSource("config1"); // 它必須依賴文件src/c3p0-config.xml
}
// 獲取c3p0連接池的連接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(Connection connection) {
close(connection, null);
}
public static void close(Connection connection, Statement statement) {
close(connection, statement, null);
}
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void destroy() {
try {
DataSources.destroy(dataSource);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
CommonUtil.java
package com.atguigu.jdbc;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class CommonUtil {
/**
* 把結果集中的每一行都放入Object對象數組中, 再把所有的Object對象數組放入一個List集合中.
* @throws SQLException
*/
public static List<Object[]> query(Connection connection, String sql, Object... values) throws SQLException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
resultSet = preparedStatement.executeQuery();
List<Object[]> list = new ArrayList<Object[]>();
int cols = resultSet.getMetaData().getColumnCount();
while (resultSet.next()) {
Object[] dataRow = new Object[cols];
for (int i = 0; i < dataRow.length; i++) {
dataRow[i] = resultSet.getObject(i + 1);
}
list.add(dataRow);
}
return list;
} finally {
JdbcUtil.close(null, preparedStatement, resultSet);
}
}
/**
* 把結果集中的第一行數據,全放入一個對象數組中
* @throws SQLException
*/
public static Object[] queryValueArray(Connection connection, String sql, Object... values) throws SQLException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
Object[] dataRow = new Object[resultSet.getMetaData().getColumnCount()];
for (int i = 0; i < dataRow.length; i++) {
dataRow[i] = resultSet.getObject(i + 1);
}
return dataRow;
} else {
return null;
}
} finally {
JdbcUtil.close(null, preparedStatement, resultSet);
}
}
/**
* 從結果集中獲取第一行的第一列
* @throws SQLException
*/
public static Object queryValue(Connection connection, String sql, Object... values) throws SQLException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return resultSet.getObject(1);
} else {
return null;
}
} finally {
JdbcUtil.close(null, preparedStatement, resultSet);
}
}
/**
* 把結果集中第一行轉換爲對象返回
* @throws SQLException
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static <T> T queryBean(Connection connection, String sql, Class<T> clazz, Object... values) throws SQLException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
T t = clazz.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
int cols = metaData.getColumnCount();
for (int i = 0; i < cols; i++) {
String label = metaData.getColumnLabel(i + 1);
Object value = resultSet.getObject(label);
Field field = clazz.getDeclaredField(label);
field.setAccessible(true);
field.set(t, value);
}
return t;
} else {
return null;
}
} finally {
JdbcUtil.close(null, preparedStatement, resultSet);
}
}
/**
* 把結果集的所有記錄都封裝成對象,並把所有對象放在一個List集合中
* @throws SQLException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws SecurityException
* @throws NoSuchFieldException
*/
public static <T> List<T> query(Connection connection, String sql, Class<T> clazz, Object... values) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
resultSet = preparedStatement.executeQuery();
List<T> list = new ArrayList<T>();
ResultSetMetaData metaData = resultSet.getMetaData();
int cols = metaData.getColumnCount();
while (resultSet.next()) {
T t = clazz.newInstance();
for (int i = 0; i < cols; i++) {
String label = metaData.getColumnLabel(i + 1);
Object value = resultSet.getObject(label);
if (value != null) {
Field field = clazz.getDeclaredField(label);
field.setAccessible(true);
field.set(t, value);
}
}
list.add(t);
}
return list;
} finally {
JdbcUtil.close(null, preparedStatement, resultSet);
}
}
/**
* 通用更新操作
* @throws SQLException
*/
public static int update(Connection connection, String sql, Object... values) throws SQLException {
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
fillArguments(preparedStatement, values);
return preparedStatement.executeUpdate();
} finally {
JdbcUtil.close(null, preparedStatement);
}
}
public static void fillArguments(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
}
BaseDAO
綜合之前學習過的知識,在這裏創建一個BaseDAO<T>類藉助DBUtils工具類實現數據操作功能:package com.atguigu.jdbc;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
public class BaseDAO<T>{
protected Class<T> clazz; // T泛型究竟是什麼類型, 用類模板對象來描述
protected QueryRunner qr = new QueryRunner(); // 用於執行通用查詢和更新的工具類對象
protected Connection connection; // 數據庫連接
protected String tableName; // 涉及到的表,需要通過構造器初始化賦值
public JdbcDAO(String tableName) {
// 以下代碼的執行者是子類對象,所以this.getClass是獲取子類的類模板對象
Type type = this.getClass().getGenericSuperclass(); // JdbcDAO<Teacher>
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;//JdbcDAO<Teacher>
Type[] types = parameterizedType.getActualTypeArguments();
clazz = (Class<T>)types[0];
} else {
clazz = (Class<T>)Object.class;
}
// 獲取一個連接供所有方法使用
try {
connection = JdbcUtil.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
this.tableName = tableName;
}
//獲得記錄中具體的一個數據
public Object getValue(String sql, Object... values) {
try {
return qr.query(connection, sql, new ScalarHandler(), values);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//獲得一行數據並封裝成javabean對象
public T get(String sql, Object... values) {
try {
return qr.query(connection, sql, new BeanHandler<T>(clazz), values);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//獲得多行記錄,封裝成javabean對象,保存在list集合中
public List<T> getList(String sql, Object... values) {
try {
return qr.query(connection, sql, new BeanListHandler<T>(clazz), values);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//獲得所有記錄,封裝成javabean對象,保存在list集合中
public List<T> getAll() {
return getList("select * from " + tableName);
}
//根據id獲取某一條記錄,並封裝成javabean對象返回
public T getById(int id) {
return get("select * from " + tableName + " where id = ?", id);
}
//根據id刪除某一條記錄,刪除成功返回ture,失敗返回false
public boolean deleteById(int id) {
int rows = update("delete from " + tableName + " where id = ?", id);
if (rows > 0) {
return true;
}
return false;
}
//通用的更新操作
public int update(String sql, Object... values) {
try {
return qr.update(connection, sql, values);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
//關閉連接
public void close() {
JdbcUtil.close(connection);
}
}