JDBC連接池和DBUtils


本節內容:

  • 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集合存放多個連接對象。如果我們需要編寫自定義連接池,需要完成以下步驟:

  1. 創建連接池(作為數據源),需要實現接口javax.sql.DataDource。因為我們只使用該接口中getConnection()方法,簡化本案例,我們只實現javax.sql.DataDource中的getConnection()方法,而沒有實現javax.sql.DataDource中的其他接口
  2. 提供一個集合,用於存放連接,因為移除/添加操作過多,所以選擇LinkedList
  3. 本案例在靜態代碼塊中,為連接池初始化5個連接(本案例中初始化連接使用上一篇文章中實現的工具類)
  4. 之后程序如果需要連接,調用實現類的getConnection(),該方法將從連接池(容器List)獲得連接。為了保證當前連接只能提供給一個線程使用,所以我們需要將連接先從連接池中移除。
  5. 當用戶使用完連接,釋放資源時,不執行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

  1. 創建類B,並實現接口A
  2. 提供類B的構造方法,參數類型A,用於接收A接口的其他實現類(C)
  3. 給類B添加類型為A成員變量,用於存放A接口的其他實現類
  4. 增強需要的方法
  5. 實現不需要增強的方法,方法里調用成員變量存放的其他實現類對應的方法

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>
c3p0-config.xml
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);
        }
    }
}
TestC3P0.java
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();
            }
        }

    }
}
JDBCUtils_V3.java

 

可以寫一個工具類,修改下相關代碼:

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);
        }
    }
}
C3P0Utils.java
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);
        }
    }

}
TestC3P0.java

 

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
db.properties
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);
        }
    }
}
DBCPUtils.java
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);
        }
    }
}
TestDBCP.java

 

二、DBUtils

如果在某些項目中,不使用框架,只使用JDBC開發,我們會發現冗余代碼過多:

為了簡化JDBC開發,我們采用apache commons組件的一個成員:DBUtils。

DBUtils就是JDBC的簡化開發工具包。需要使用的技術:連接池(只用到里面的獲得連接),SQL語句並沒有變少以及設置sql語句中的參數。

 

1. JavaBean組件

JavaBean就是一個類,在開發中常用語封裝數據。具有如下特性:

  1. 需要實現接口:java.io.Serializable,通常偷懶省略了。
  2. 提供私有字段:private 類型 字段名;
  3. 提供getter/setter方法
  4. 提供無參構造

一般將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;
    }

}
User.java
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);
        }
    }
}
C3P0Utils.java
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();
        }
    }
}
TestDBUtils1.java --添加、刪除、更新
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);
        }
    }
}
TestDBUtils2.java --查詢操作

 

【注意】:DBUtils在創建QueryRunner時傳入dataSource對象,每次在執行完之后都會自動關閉Connection連接對象。(就相當於把conn交給dbutils管理了,他會幫我們關掉)

如果沒有傳入dataSource的話,需要手動關閉。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM