JDBC學習筆記——簡單的連接池


1、使用LinkedList保存連接                                                               

      即使是最簡單的JDBC操作,也需要包含以下幾步:建立連接、創建SQL語句、執行語句、處理執行結果、釋放資源,其中建立連接步驟是很耗費計算機性能的,如果我們每次進行JDBC操作都創建新的JDBC連接,使用完后再立即釋放連接,這樣做會耗費大量性能。更合理的做法應該是:創建JDBC連接,使用JDBC連接,使用完后不是立刻釋放JDBC連接,而是把連接緩存起來,當下一次操作JDBC時,我們可以直接使用緩存中已經連接上的JDBC連接。

      以下的代碼通過一個LinkedList保存創建的JDBC連接,在剛創建的時候,我們會先建立10個連接並保存在list中,當客戶代碼需要使用Connection的時候,我們直接從list中取出第一個Connection返回,當客戶代碼使用完Connection,需要調用free()方法,把Connection再放回list中:

public class DataSource1 {
	LinkedList<Connection> connectionPool = new LinkedList<Connection>();

	public DataSource1() {
		try {
			for (int i = 0; i < 10; i++) {
				this.connectionPool.addLast(this.createConnection());
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}
	
	public Connection getConnection() {
		return connectionPool.removeFirst();
	}
	
	public void free(Connection conn) {
		connectionPool.addLast(conn);
	}
	
	private Connection createConnection() throws SQLException {
		return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
				"root", "");
	}
}

2、控制連接數量                                                                             

      在上例的getConnection()方法中,我們沒有檢測connectionPool中是否包含Connection,直接返回connectionPool中的第一個Connection,這樣做是不安全的,在從connectionPool中返回Connection之前,我們首先應該檢測connectionPool中是否有Connection,若有,返回connectionPool中的第一個Connection,如果沒有,我們可以新創建一個並返回。但是這樣有一個問題,如果請求的線程很多,我們這樣無限制地創建很多Connection可能導致數據庫阻塞,因為數據庫可以支持的連接數是有限的,所以我們應該控制新建Connection的上限,若沒有達到上限,我們可以創建並返回,如果達到連接上限,那么我們就拋出異常。同時我們應該在getConnection()上加鎖,保證多線程安全性,修改后代碼如下,我們用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示現在存活的Connection數量:

public class DataSource2 {
	private static int initCount = 10;
	private static int maxCount = 30;
	private int currentCount = 0;

	LinkedList<Connection> connectionPool = new LinkedList<Connection>();

	public DataSource2() {
		try {
			for (int i = 0; i < initCount; i++) {
				this.connectionPool.addLast(this.createConnection());
				this.currentCount++;
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public Connection getConnection() throws SQLException {
		synchronized (connectionPool) {
			if (connectionPool.size() > 0)
				return connectionPool.removeFirst();

			if (this.currentCount < maxCount) {
				this.currentCount++;
				return createConnection();
			}

			throw new SQLException("已沒有鏈接");
		}
	}

	public void free(Connection conn) {
		connectionPool.addLast(conn);
	}

	private Connection createConnection() throws SQLException {
		return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
				"root", "");
	}
}

3、動態代理攔截close方法                                                          

      上面的代碼有個問題,那就是要關閉Connection必須調用我們的free()方法,不能直接調用Connection上的close()方法,這對於一些習慣使用完Connection就close()的用戶並不是很友好,很容易導致它們忘記把用完的Connection還回connectionPool,為了讓用戶保持原有習慣,我們希望能夠改寫Connection的close()方法,讓它不是直接關閉連接,而是把連接還回connectionPool中,其他方法保持原來不變,對於這種需求,我們可以使用動態代理實現:

public class DataSource3 {
	private static int initCount = 1;
	private static int maxCount = 1;
	int currentCount = 0;

	LinkedList<Connection> connectionsPool = new LinkedList<Connection>();

	public DataSource3() {
		try {
			for (int i = 0; i < initCount; i++) {
				this.connectionsPool.addLast(this.createConnection());
				this.currentCount++;
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public Connection getConnection() throws SQLException {
		synchronized (connectionsPool) {
			if (this.connectionsPool.size() > 0)
				return this.connectionsPool.removeFirst();

			if (this.currentCount < maxCount) {
				this.currentCount++;
				return this.createConnection();
			}

			throw new SQLException("已沒有鏈接");
		}
	}

	public void free(Connection conn) {
		this.connectionsPool.addLast(conn);
	}

	private Connection createConnection() throws SQLException {
		Connection realConn = DriverManager.getConnection(
				"jdbc:mysql://localhost:3306/jdbc", "root", "");
		MyConnectionHandler proxy = new MyConnectionHandler(this);
		return proxy.bind(realConn);
	}
}


class MyConnectionHandler implements InvocationHandler {
	private Connection realConnection;
	private Connection warpedConnection;
	private DataSource3 dataSource;

	MyConnectionHandler(DataSource3 dataSource) {
		this.dataSource = dataSource;
	}

	Connection bind(Connection realConn) {
		this.realConnection = realConn;
		this.warpedConnection = (Connection) Proxy.newProxyInstance(this
				.getClass().getClassLoader(), new Class[] { Connection.class },
				this);
		return warpedConnection;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if ("close".equals(method.getName())) {
			this.dataSource.connectionsPool.addLast(this.warpedConnection);
		}
		return method.invoke(this.realConnection, args);
	}
}

  上述代碼的關鍵點是MyConnectionHandler類,在該類的bind()方法中返回了一個wrapedConnection,wrapedConnection的創建方法如下:

this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), 
        new Class[] { Connection.class },this);

  Proxy.newProxyInstance()方法是Java動態代理的關鍵方法,該方法會在運行時在內存中動態創建一個類,該方法的第一個參數是指定一個ClassLoader,第二個參數指定動態創建類實現的接口,第三個參數指定在該類上調用的方法應該轉給哪個類,在這里指定了this,所以所有方法都會轉給MyConnectionHandler,准確的說是MyConnectionHandler的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
	if ("close".equals(method.getName())) {
		this.dataSource.connectionsPool.addLast(this.warpedConnection);
	}
	return method.invoke(this.realConnection, args);
}

  在invoke()方法中,我們可以看到第二個參數傳遞了調用的Method,我們根據傳遞的Method對象判斷,用戶調用的是否是close方法,如果是close方法,那么我們就把這個Connection重新加入list,如果不是,那么我們就執行真正Connection上的相應方法。

      還需要注意的是,現在調用createConnection()產生的Connection對象已經不是原始的Connection對象,而是調用MyConnectionHandler類上bind()方法動態產生的代理類:

private Connection createConnection() throws SQLException {
	Connection realConn = DriverManager.getConnection(
			"jdbc:mysql://localhost:3306/jdbc", "root", "");
	MyConnectionHandler proxy = new MyConnectionHandler(this);
	return proxy.bind(realConn);
}

 


免責聲明!

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



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