JDBC学習ノート——簡単な接続池


1、リンクをLinked Listで保存する                                                              
      最も簡単なJDBC操作であっても、接続を確立し、SQL文を作成し、実行文を作成し、処理実行結果、資源を解放し、接続手順を確立するのはコンピュータの性能がかかります.JDBC操作を行うたびに新しいJDBC接続を作成し、使用後すぐに接続を解放すると、大量の性能がかかります.より合理的な方法は、JDBC接続を作成し、JDBC接続を使用して、使用後すぐにJDBC接続を解放するのではなく、接続をキャッシュして、次の操作でJDBC接続を直接使用して、キャッシュに接続されているJDBC接続を使用することです.
      次のコードはLinkdListによって作成されたJDBC接続を保存します.作成したばかりの時、まず10個の接続を確立してlistに保存します.顧客コードがConnectionを使用する必要がある時、直接リストから最初のConnectionを取り出して返します.顧客コードがConnectionを使用し終わったら、freeを呼び出す必要があります.
public class DataSource1 {
	LinkedList connectionPool = new LinkedList();

	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、接続数を制御する                                                                          
      上記のget Connectionの方法では、connection PoolにConnectionが含まれているかどうかは検出されていません.直接にconnection Poolの中の最初のConnectionに戻ります.このようにするのは安全ではありません.connection PoolからConnectionに戻る前に、まずConnection Poolの中にConnectionがあるかどうかを確認します.もしないなら、新しく作成して返してもいいです.しかし、このような問題があります.要求スレッドが多い場合、私達はこのように無制限に多くのConnectionを作成して、データベースがサポートできる接続数が限られているため、新しいConnectionの上限を制御しなければなりません.上限に達していなければ、作成して戻すことができます.もし接続の上限に達したら、じゃ、異常を捨てよう.同時にget Connectionにロックをかけて、マルチスレッドの安全性を保証します.修正後のコードは以下の通りです.私達はinitCountでlist初期化サイズを表します.maxCountはlist最大サイズを表します.currentCountは現在生存しているConnectionの数を表します.
public class DataSource2 {
	private static int initCount = 10;
	private static int maxCount = 30;
	private int currentCount = 0;

	LinkedList connectionPool = new LinkedList();

	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(free)を呼び出さなければなりません.Connection上のcloseメソッドを直接呼び出すことはできません.これはいくつかの習慣で使用したConnectionをclose()のユーザーにとって友好的ではなく、使い終わったConnectionをコンサートPoolに返すことを忘れてしまいます.私たちはConnectionのclose()方法を書き換えて、直接接続をオフにするのではなく、接続をconnection Poolに戻すことを望んでいます.他の方法はもとのままにしておきたいです.このような需要に対して、ダイナミックエージェントを使って実現できます.
public class DataSource3 {
	private static int initCount = 1;
	private static int maxCount = 1;
	int currentCount = 0;

	LinkedList connectionsPool = new LinkedList();

	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);
	}
}
上記のコードのポイントはMyConnection Handler類であり、この種類のbind()方法の中で一つのwrapedConnectionを返しました.wrapedConnectionの作成方法は以下の通りです.
this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), 
        new Class[] { Connection.class },this);
Proxy.newProxyInstance()はJavaダイナミックエージェントのキーメソッドであり、この方法は実行時にメモリ内で動的にクラスを作成します.この方法の最初のパラメータはClass Loaderを指定し、第二のパラメータは動的作成クラスの実現インターフェースを指定します.第三のパラメータはこのクラスで呼び出す方法をどのクラスに変えるべきかを指定します.ここでthisを指定します.したがって、すべての方法はMyConnection Handlerに転送されます.正確にはMyConnection Handlerの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オブジェクトではなく、MyConnection Handler類で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);
}
 
転載先:https://www.cnblogs.com/timlearn/p/4080169.html