本節內容:
- JDBC連接池
- DBUtils
一、JDBC連接池
實際開發中“獲得連接”或“釋放資源”是非常消耗系統資源的兩個過程,為了解決此類性能問題,通常情況我們采取連接池技術,來共享連接Connection。在程序開始的時候,可以創建幾個連接,將連接放入到連接池中。用戶使用連接的時候,可以從連接池中進行獲取。用完之后,可以將連接歸還連接池。
連接池主要解決的是性能問題。
1. 連接池概述
(1)概念
用池來管理Connection,這樣可以重復使用Connection。有了池,我們就不用自己來創建Connection,而是通過池來獲取Connection對象。當使用完Connection后,調用Connection的close()方法也不會真的關閉Connection,而是把Connection“歸還”給池。池就可以再利用這個Connection對象了。

(2)規范
Java為數據庫連接池提供了公共的接口,javax.sql.DataSource,各個廠商需要讓自己的連接池實現這個接口。這樣應用程序可以方便地切換不同廠商的連接池。
常見的連接池:DBCP、C3P0。還有tomcat自帶的JNDI,JNDI使用的概率極低。市場上使用率最高的是C3P0。
2. 自定義連接池(了解)
定義一個連接池實現javax.sql.DataSource,使用List集合存放多個連接對象。如果我們需要編寫自定義連接池,需要完成以下步驟:
- 創建連接池(作為數據源),需要實現接口javax.sql.DataDource。因為我們只使用該接口中getConnection()方法,簡化本案例,我們只實現javax.sql.DataDource中的getConnection()方法,而沒有實現javax.sql.DataDource中的其他接口
- 提供一個集合,用於存放連接,因為移除/添加操作過多,所以選擇LinkedList
- 本案例在靜態代碼塊中,為連接池初始化5個連接(本案例中初始化連接使用上一篇文章中實現的工具類)
- 之后程序如果需要連接,調用實現類的getConnection(),該方法將從連接池(容器List)獲得連接。為了保證當前連接只能提供給一個線程使用,所以我們需要將連接先從連接池中移除。
- 當用戶使用完連接,釋放資源時,不執行close()方法,而是將連接添加到連接池中。
MyDataSource.java
package cn.itheima.jdbc.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
import cn.itheima.jdbc.utils.JDBCUtils_V3;
public class MyDataSource implements DataSource{
//1.創建1個容器用於存儲Connection對象
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//2.創建5個連接放到容器中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils_V3.getConnection();
pool.add(conn);
}
}
/**
* 重寫獲取連接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判斷
if(pool.size()==0){
//4.池子里面沒有,我們再創建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils_V3.getConnection();
pool.add(conn);
}
}
//5.從池子里面獲取一個連接對象Connection
conn = pool.remove(0);
return conn;
}
/**
* 歸還連接對象到連接池中去
*/
public void backConnection(Connection conn){
pool.add(conn);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
TestMyDataSource.java
package cn.itheima.jdbc.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import cn.itheima.jdbc.DataSource.MyDataSource;
import cn.itheima.jdbc.DataSource.MyDataSource1;
import cn.itheima.jdbc.utils.JDBCUtils_V3;
public class TestMyDataSource {
/**
* 添加用戶
* 使用未改造過的connection
*/
@Test
public void testAddUser() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.創建自定義連接池對象
MyDataSource dataSource = new MyDataSource();
try {
// 2.從池子中獲取連接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "呂布");
pstmt.setString(2, "貂蟬");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失敗!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
dataSource.backConnection(conn);
}
}
}
JDBCUtils_V3.java
package cn.itheima.jdbc.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* 提供獲取連接和釋放資源的 方法
*
* @author Never Say Never
* @date 2017年10月29日
* @version V1.0
*/
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password;
/**
* 靜態代碼塊加載配置文件信息
*/
static {
try {
// 1.通過當前類獲取類加載器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通過類加載器的方法獲得一個輸入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.創建一個properties對象
Properties props = new Properties();
// 4.加載輸入流
props.load(is);
// 5.獲取相關參數的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 獲取連接方法
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源方法
*
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3. 自定義連接池(方法增強)
上面的自定義連接池示例中存在嚴重問題,用戶調用getConnection()獲得連接后,必須使用backConnection(conn)方法進行連接的歸還,如果用戶調用conn.close()將連接真正的釋放,連接池中將出現無連接可用。
我們希望用戶即使調用了close()方法,連接仍歸還給連接池。我們需要對close()方法進行增強,從而實現將連接歸還給連接池的功能。
(1)方法增強的方式
- 繼承:子類繼承父類,將父類的方法進行重寫,從而進行增強
- 使用前提:必須有父類,且存在繼承關系。
- 裝飾者設計模式,此設計模式專門用於增強方法
- 使用前提:必須有接口
- 缺點:需要將接口的所有方法都實現
- 動態代理:在運行時動態的創建代理類,完成增強操作,與裝飾者類似。
- 使用前提:必須有接口
- 難點:需要反射技術
- 字節碼增強,運行時創建目標類子類,從而進行增強
- 常見第三方框架:cglib、javassist等
(2)裝飾者設計模式
設計模式:專門為解決某一類問題,而編寫的固定格式的代碼。
裝飾者固定結構:接口A,已知實現類C,需要裝飾者創建代理類B
- 創建類B,並實現接口A
- 提供類B的構造方法,參數類型A,用於接收A接口的其他實現類(C)
- 給類B添加類型為A成員變量,用於存放A接口的其他實現類
- 增強需要的方法
- 實現不需要增強的方法,方法里調用成員變量存放的其他實現類對應的方法
MyConnection.java
package cn.itheima.jdbc.DataSource;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
//1.實現同一個接口Connection
public class MyConnection implements Connection {
//3.定義一個變量
private Connection conn;
private LinkedList<Connection> pool;
// 2.編寫一個構造方法(參數使用了面相對象的多態特性)
public MyConnection(Connection conn,LinkedList<Connection> pool) {
this.conn=conn;
this.pool=pool;
}
//4.書寫需要增強的方法
@Override
public void close() throws SQLException {
pool.add(conn);
}
/**
* 此方法必須覆蓋!否則會出現空指針異常!!!
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Statement createStatement() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void commit() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void rollback() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isClosed() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isReadOnly() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getTransactionIsolation() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void clearWarnings() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setHoldability(int holdability) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getHoldability() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Clob createClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Blob createBlob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public NClob createNClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public String getClientInfo(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public String getSchema() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getNetworkTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
}
MyDataSource1.java
package cn.itheima.jdbc.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
import cn.itheima.jdbc.utils.JDBCUtils_V3;
public class MyDataSource1 implements DataSource{
//1.創建1個容器用於存儲Connection對象
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//2.創建5個連接放到容器中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils_V3.getConnection();
//放入池子中connection對象已經經過改造了
MyConnection myconn = new MyConnection(conn, pool);
pool.add(myconn);
}
}
/**
* 重寫獲取連接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判斷
if(pool.size()==0){
//4.池子里面沒有,我們再創建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils_V3.getConnection();
//放入池子中connection對象已經經過改造了
MyConnection myconn = new MyConnection(conn, pool);
pool.add(myconn);
}
}
//5.從池子里面獲取一個連接對象Connection
conn = pool.remove(0);
return conn;
}
/**
* 歸還連接對象到連接池中去
*/
public void backConnection(Connection conn){
pool.add(conn);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
TestMyDataSource.java
package cn.itheima.jdbc.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import cn.itheima.jdbc.DataSource.MyDataSource;
import cn.itheima.jdbc.DataSource.MyDataSource1;
import cn.itheima.jdbc.utils.JDBCUtils_V3;
public class TestMyDataSource {
/**
* 添加用戶
* 使用未改造過的connection
*/
@Test
public void testAddUser() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.創建自定義連接池對象
MyDataSource dataSource = new MyDataSource();
try {
// 2.從池子中獲取連接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "呂布");
pstmt.setString(2, "貂蟬");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失敗!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
dataSource.backConnection(conn);
}
}
/**
* 添加用戶
* 使用改造過的connection
*/
@Test
public void testAddUser1() {
Connection conn = null;
PreparedStatement pstmt = null;
// 1.創建自定義連接池對象
DataSource dataSource = new MyDataSource1();
try {
// 2.從池子中獲取連接
conn = dataSource.getConnection();
String sql = "insert into user values(null,?,?)";
//3.必須在自定義的connection類中重寫prepareStatement(sql)方法
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "呂布1");
pstmt.setString(2, "貂蟬1");
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失敗!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
}
}
JDBCUtils_V3.java
package cn.itheima.jdbc.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* 提供獲取連接和釋放資源的 方法
*
* @author Never Say Never
* @date 2017年10月29日
* @version V1.0
*/
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password;
/**
* 靜態代碼塊加載配置文件信息
*/
static {
try {
// 1.通過當前類獲取類加載器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通過類加載器的方法獲得一個輸入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.創建一個properties對象
Properties props = new Properties();
// 4.加載輸入流
props.load(is);
// 5.獲取相關參數的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 獲取連接方法
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源方法
*
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4. C3P0連接池
C3P0開源免費的連接池。目前使用它的開源項目有:Spring、Hibernate等。使用第三方工具需要導入jar包,c3p0使用時可以添加配置文件c3p0-config.xml(或者c3p0.properties,大部分使用c3p0-config.xml),也可以不添加配置文件。它有兩種使用方式,但是多數情況下都是用需要配置文件的那種方式。
(1)導入jar包
如果使用的是0.9.2版本,需要導入兩個jar包。

如果使用的是0.9.1版本,只需要導入c3p0-0.9.1.2.jar。
(2)配置文件
- 配置文件名稱:c3p0-config.xml(固定)
- 配置文件位置:src(類路徑)
- 配置文件內容:命名配置
<c3p0-config> <named-config name="itheima"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///web08</property> <property name="user">root</property> <property name="password">123456</property> </named-config> </c3p0-config>
- 配置文件內容:默認配置
<c3p0-config> <default-config> <property name="automaticTestTable">con_test</property> <property name="checkoutTimeout">30000</property> <property name="idleConnectionTestPeriod">30</property> <property name="initialPoolSize">10</property> <property name="maxIdleTime">30</property> <property name="maxPoolSize">100</property> <property name="minPoolSize">10</property> <property name="maxStatements">200</property> </default-config> </c3p0-config>
(3)示例
c3p0提供核心工具類:ComboPooledDataSource,如果要使用連接池,必須創建該類的實例對象。
- new ComboPooledDataSource("itcast"); 使用配置文件“命名配置”
- <name-config name="itcast">
- new ComboPooledDataSource(); 使用配置文件“默認配置”
- <default-config>
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///web08</property> <property name="user">root</property> <property name="password">123456</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> <named-config name="itheima"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///web08</property> <property name="user">root</property> <property name="password">123456</property> </named-config> </c3p0-config>
import java.sql.Connection; import java.sql.PreparedStatement; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestC3P0 { @Test public void testAddUser() { Connection conn = null; PreparedStatement pstmt = null; // 1.創建自定義連接池對象 ComboPooledDataSource dataSource = new ComboPooledDataSource();// 加載默認的配置 // ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");//加載有名稱的配置 try { // 2.從池子中獲取連接 conn = dataSource.getConnection(); String sql = "insert into tbl_user values(null,?,?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, "呂布2"); pstmt.setString(2, "貂蟬2"); int rows = pstmt.executeUpdate(); if (rows > 0) { System.out.println("添加成功!"); } else { System.out.println("添加失敗!"); } } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils_V3.release(conn, pstmt, null); } } }
import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import java.util.ResourceBundle; /** * 提供獲取連接和釋放資源的 方法 * * @author Never Say Never * @date 2017年10月29日 * @version V1.0 */ public class JDBCUtils_V3 { private static String driver; private static String url; private static String username; private static String password; /** * 靜態代碼塊加載配置文件信息 */ static { try { // 1.通過當前類獲取類加載器 ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader(); // 2.通過類加載器的方法獲得一個輸入流 InputStream is = classLoader.getResourceAsStream("db.properties"); // 3.創建一個properties對象 Properties props = new Properties(); // 4.加載輸入流 props.load(is); // 5.獲取相關參數的值 driver = props.getProperty("driver"); url = props.getProperty("url"); username = props.getProperty("username"); password = props.getProperty("password"); } catch (IOException e) { e.printStackTrace(); } } /** * 獲取連接方法 * * @return */ public static Connection getConnection() { Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 釋放資源方法 * * @param conn * @param pstmt * @param rs */ public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pstmt != null) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
可以寫一個工具類,修改下相關代碼:
import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Utils { private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima"); public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } }
import java.sql.Connection; import java.sql.PreparedStatement; import org.junit.Test; import com.mchange.v2.c3p0.ComboPooledDataSource; import cn.itheima.jdbc.utils.C3P0Utils; import cn.itheima.jdbc.utils.JDBCUtils_V3; public class TestC3P0 { @Test public void testAddUser1() { Connection conn = null; PreparedStatement pstmt = null; try { // 2.從池子中獲取連接 conn = C3P0Utils.getConnection(); String sql = "insert into tbl_user values(null,?,?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, "呂布3"); pstmt.setString(2, "貂蟬3"); int rows = pstmt.executeUpdate(); if (rows > 0) { System.out.println("添加成功!"); } else { System.out.println("添加失敗!"); } } catch (Exception e) { throw new RuntimeException(e); } finally { JDBCUtils_V3.release(conn, pstmt, null); } } }
5. DBCP連接池
DBCP也是一個開源的連接池,是Apache上的一個java連接池項目,在企業開發中也比較常見,tomcat內置的連接池。
(1)導入jar包
commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
(2)配置文件
- 配置文件名稱:*.properties
- 配置文件位置:任意,建議src(classpath/類路徑)
- 配置文件內容:properties不能寫中文,不支持在STS(Eclipse的一個變種)中修改,必須使用記事本修改內容,否則中文注釋就亂碼了
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=utf8 username=root password=123456
import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; public class DBCPUtils { private static DataSource dataSource; static{ try { //1.加載找properties文件輸入流 InputStream is = DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties"); //2.加載輸入流 Properties props = new Properties(); props.load(is); //3.創建數據源 dataSource = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new RuntimeException(e); } } public static DataSource getDataSource(){ return dataSource; } public static Connection getConnection(){ try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } }
import java.sql.Connection; import java.sql.PreparedStatement; import org.junit.Test; import cn.itheima.jdbc.utils.DBCPUtils; public class TestDBCP { @Test public void testUpdateUserById(){ Connection conn = null; PreparedStatement pstmt = null; try { conn = DBCPUtils.getConnection(); String sql ="update tbl_user set upassword=? where uid=?"; pstmt= conn.prepareStatement(sql); pstmt.setString(1, "柳岩"); pstmt.setInt(2, 20); int rows = pstmt.executeUpdate(); if(rows>0){ System.out.println("更新成功!"); }else{ System.out.println("更新失敗!"); } } catch (Exception e) { throw new RuntimeException(e); } } }
二、DBUtils
如果在某些項目中,不使用框架,只使用JDBC開發,我們會發現冗余代碼過多:

為了簡化JDBC開發,我們采用apache commons組件的一個成員:DBUtils。
DBUtils就是JDBC的簡化開發工具包。需要使用的技術:連接池(只用到里面的獲得連接),SQL語句並沒有變少以及設置sql語句中的參數。
1. JavaBean組件
JavaBean就是一個類,在開發中常用語封裝數據。具有如下特性:
- 需要實現接口:java.io.Serializable,通常偷懶省略了。
- 提供私有字段:private 類型 字段名;
- 提供getter/setter方法
- 提供無參構造
一般將JavaBean類放到domain包或model包里。
2. DBUtils介紹
(1)概述
DBUtils封裝了對JDBC的操作,簡化了JDBC操作,可以少寫代碼。在不使用框架的情況下,使用DBUtils的概率非常大。
DBUtils三個核心功能介紹:
- QueryRunner類,提供對sql語句操作的API
- ResultSetHandler接口,用於定義select操作后,怎樣封裝結果集
- DbUtils類,它就是一個工具類,定義了關閉資源與事務處理的方法
(2)QueryRunner核心類
- QueryRunner(Database ds),提供了數據源(連接池),DBUtils底層自動維護connection
- update(String sql, Object... params),執行更新數據。(增、刪、改)
- query(String sql, ResultSetHandler<T> rsh, Object... params),執行查詢
(3)ResultSetHandler結果處理集類
我們知道在執行select語句之后得到的是ResultSet,然后我們還需要對ResultSet進行轉換,得到最終我們想要的數據。你可能希望把ResultSet的數據放到一個List中,也可能想把數據放到一個Map中,或是一個Bean中。
DBUtils提供了一個接口ResultSetHandler,它就是用來ResultSet轉換成目標類型的工具。你可以自己去實現這個接口,把ResultSet轉換成你想要的類型。
DBUtils提供了很多個ResultSetHandler接口的實現,這些實現已經基本夠用了,我們通常不用自己去實現ResultSet接口了。
- MapHandler:單行處理器!把結果集轉換成Map<String,Object>,其中列名為鍵
- MapListHandler:多行處理器!把結果集轉換成List<Map<String,Object>>;
- BeanHandler:單行處理器!把結果集轉換成Bean,該處理器需要Class參數,即Bean的類型;
- BeanListHandler:多行處理器!把結果集轉換成List<Bean>;
- ColumnListHandler:多行單列處理器!把結果集轉換成List<Object>,使用ColumnListHandler時需要指定某一列的名稱或編號,例如:new ColumListHandler(“name”)表示把name列的數據放到List中。
- ScalarHandler:單行單列處理器!把結果集轉換成Object。一般用於聚集查詢,例如select count(*) from tab_student。
| ArrayHandler | 將結果集中的第一條記錄封裝到一個Object[]數組中,數組中的每一個元素就是這條記錄中的每一個字段的值 |
| ArrayListHangler | 將結果集中的第一條記錄封裝到一個Object[]數組中,將這些數組在封裝到List集合中 |
| BeanHandler | 將結果集中的每一條記錄封裝到一個指定的javaBean中 |
| BeanListHandler | 將結果集中每一條記錄封裝到指定的javaBean中,將這些JavaBean在封裝到List集合中 |
| ColumnListHandler | 將結果集中指定的列的字段值,封裝到一個List集合中 |
| KeyedHandler | 將結果集中每一條記錄封裝到Map<String,Object>,在將這個map結婚作為另一個Map的value,另一個Map集合的key是指定的字段的名稱 |
| MapHandler | 將結果集中第一條記錄封裝到Map<String,Object>集合中,key就是字段名稱,value就是字段值 |
| MapListHandler | 將結果集中每一條記錄封裝到了Map<String,Object>集合中,key就是字段名稱,value就是字段值,在將這些Map封裝到List集合中 |
| ScalarHandler | 它用於單數據。例如select count(*) from table 操作 |
Map處理器:
每一條記錄作為一個Map,Map的key是字段名稱。

Bean處理器 :
必須搞一個javaBean。

Column處理器:

Scalar處理器:
(4)DbUtils工具類
- closeQuietly(Connection conn) 關閉連接,如果有異常try后不拋
- commitAndCloseQuietly(Connection conn) 提交並關閉連接
- rollbackAndCloseQuietly(Connection conn) 回滾並關閉連接
3. 示例:使用DBUtils完成增刪改查操作
public class User { private int uid; private String uname; private String upassword; public User() { } public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUpassword() { return upassword; } public void setUpassword(String upassword) { this.upassword = upassword; } }
import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Utils { private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima"); public static DataSource getDataSource() { return dataSource; } public static Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } } }
import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.junit.Test; import cn.itheima.jdbc.utils.C3P0Utils; /** * 測試DBUtils工具類的增刪改操作 * * @author Never Say Never * @date 2017年10月31日 * @version V1.0 */ public class TestDBUtils1 { /** * 添加所有用戶方法 */ @Test public void testAddUser() { try { // 1.創建核心類QueryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫SQL語句 String sql = "insert into user values(null,?,?)"; // 3.為占位符設置值 Object[] params = { "余淮", "耿耿" }; // 4.執行添加操作 int rows = qr.update(sql, params); if (rows > 0) { System.out.println("添加成功!"); } else { System.out.println("添加失敗!"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 根據id修改用戶方法 * */ @Test public void testUpdateUserById() { try { // 1.創建核心類QueryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫SQL語句 String sql = "update user set upassword=? where uid=?"; // 3.為站位符設置值 Object[] params = { "xxx", 21 }; // 4.執行添加操作 int rows = qr.update(sql, params); if (rows > 0) { System.out.println("修改成功!"); } else { System.out.println("修改失敗!"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 根據id刪除用戶方法 */ @Test public void testDeleteUserById() { try { // 1.創建核心類QueryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫SQL語句 String sql = "delete from user where uid=?"; // 3.為站位符設置值 Object[] params = {19}; // 4.執行添加操作 int rows = qr.update(sql, params); if (rows > 0) { System.out.println("刪除成功!"); } else { System.out.println("刪除失敗!"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
import java.sql.SQLException; import java.util.List; import java.util.Map; 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.ColumnListHandler; import org.apache.commons.dbutils.handlers.MapListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import org.junit.Test; import cn.itheima.domain.User; import cn.itheima.jdbc.utils.C3P0Utils; /** * 測試DBUtils查詢操作 * * @author Never Say Never * @date 2017年10月31日 * @version V1.0 */ public class TestDBUtils2 { /* * 查詢所有用戶方法 */ @Test public void testQueryAll() { try { // 1.獲取核心類queryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫sql語句 String sql = "select * from user"; // 3.執行查詢操作 List<User> users = qr.query(sql, new BeanListHandler<User>(User.class)); // 4.對結果集集合進行遍歷 for (User user : users) { System.out.println(user.getUname() + " : " + user.getUpassword()); } } catch (SQLException e) { throw new RuntimeException(e); } } /* * 根據id查詢用戶方法 */ @Test public void testQueryUserById() { try { // 1.獲取核心類queryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫sql語句 String sql = "select * from user where uid=?"; //3.為占位符設置值 Object[] params = {21}; // 4.執行查詢操作 User user = qr.query(sql, new BeanHandler<User>(User.class), params); System.out.println(user.getUname() + " : " + user.getUpassword()); } catch (SQLException e) { throw new RuntimeException(e); } } /* * 根據所有用戶的總個數 */ @Test public void testQueryCount() { try { // 1.獲取核心類queryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫sql語句 String sql = "select count(*) from tbl_user"; // 4.執行查詢操作 Long count = (Long) qr.query(sql, new ScalarHandler()); System.out.println(count); } catch (SQLException e) { throw new RuntimeException(e); } } /* * 查詢所有用戶方法 */ @Test public void testQueryAll1() { try { // 1.獲取核心類queryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫sql語句 String sql = "select * from user"; // 3.執行查詢操作 List<Map<String, Object>> list = qr.query(sql, new MapListHandler()); // 4.對結果集集合進行遍歷 for (Map<String, Object> map : list) { System.out.println(map); } } catch (SQLException e) { throw new RuntimeException(e); } } /* * 查詢所有用戶方法 */ @Test public void testQueryAll2() { try { // 1.獲取核心類queryRunner QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource()); // 2.編寫sql語句 String sql = "select * from user"; // 3.執行查詢操作 List<Object> list = qr.query(sql, new ColumnListHandler("uname")); // 4.對結果集集合進行遍歷 for (Object object : list) { System.out.println(object); } } catch (SQLException e) { throw new RuntimeException(e); } } }
【注意】:DBUtils在創建QueryRunner時傳入dataSource對象,每次在執行完之后都會自動關閉Connection連接對象。(就相當於把conn交給dbutils管理了,他會幫我們關掉)
如果沒有傳入dataSource的話,需要手動關閉。
