光プールとは何ですか?
BlueSky の投稿にあるこの単純な質問から、非常に素晴らしいと思う説明が得られました。それを終わらせるためにここに来ました。
具体的な文脈では、光接続プールが話題になっていました。しかし、Hikari が接続プールである場合、「プール」とは何でしょうか?
HikariCP とは何かを説明する前に、接続プール とは何かを説明する必要があります。 接続プールを説明するには、プールを説明する必要があります。
これを経済に例えてみましょうか?歴史的な経済アナロジーは現実世界との欠陥や不正確さに満ちていますが、説明を聞いただけでその不信感をすぐに止めてください。自己完結型です。
あなたが中世の領主/貴婦人であると想像してください。あなたは農民の仕事を遂行するための道具を持っています。そして、あなたは彼らに働いてもらいたいのです。では、どうやってこれを保証するのでしょうか?ツールがあなたのものなら?簡単なことですが、道具を農民に届ける必要があります。
それでは、状況を想像してみてください。あなたのお百姓さんが土地の草取りに鍬が必要なので、そこへ行ってあなたに鍬を求めます。あなたは彼に鍬を与えて生き続けるでしょう。しかし、彼がそれを返さなかったら、彼の在庫のクワはどうなるでしょうか?いつかは終わりが来る…
鍬を引き渡す代わりに、鍬を作ってもらうこともできます。あなたはそれらの土地の領主/淑女なので、鍛冶屋にアクセスして金属を製錬して鍬の形にし、ハンドルにはめ込むことができます。しかし、これは農民が待合室に座っていなければ、すぐに生産できるものではありません。この新しい機能を作成するには、膨大な時間とエネルギーが必要です。
これで、農民が一日の終わりに鍬を返却すると、翌日には別の農民がその鍬を使用できるようになります。
ここでは、クワのプールを制御しています。 プール は、次のアクションを実行できることを示すデザイン パターンです:
オブジェクトのプールに含めるその他の一般的なもの:
さて、HikariCPに近づいてみましょう。ここでは Java でのデータベース接続について話しましょう。
Java では、データベースへの接続を確立するように求められます。直接接続オプションがあります。これを呼び出すクラスと詳細について直接理解する必要があります。そうでない場合は、単純にサービス検出オプションを利用してください。
サービス ディスカバリーを使用するには、アプリオリに、サービス プロバイダーが提供しているものを登録する方法を作成し、その後、「サービス ディスカバリー」がそれを追跡して、誰がそのリクエストに対応できるかを確認します。
データベースと通信するために JDBC 接続を確立する必要がある場合がありました。ただし、私の JDBC ドライバーは値として null を使用できず、クエリ内で直接 null を使用することのみを受け入れました。それで、私は何をしましたか?ドライバーの上にドライバーが!
一般的なアイデアは次のとおりです。値を挿入したい次のクエリがあると想像してください:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
ここで、この値を銀行に初めて挿入する処理をしていると想像してください。これを行うには、ID=1、CONTENT=first、PARENT=null のままにしておく必要があります。結局のところ、そのような親レコードは存在しないからです (結局のところ、これが最初です)。
自然に行われること:
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
このまま使い続けたいです、やっぱり慣用的な使い方ですね。 CUPID によれば、I は「慣用的な」から来ています。慣用的なコードを持つという考え方は、まさに「不必要な精神的負荷を軽減する」ためです。
これを解決するために、私の選択は、executeUpdate の直前まで prepareStatement をそのままにしておくことでした。そこで、適用するすべての null を保存し、実際に null が必要であることに気付いたときに、文字列置換を実行して新しいクエリを生成すると、この新しいクエリが実際に実行されます。
この場合、次のことから始めます:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
したがって、次の値を入力する必要があります:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
しかし、実際には null を使用することはできないので、3 番目の場所が null であることを識別するキーを作成します。
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
この場合、この新しい文字列を準備し、要求された内容に従って引数を配置します。
そうは言っても、JDBC ドライバーを使用する必要があることをアプリケーションに示すにはどうすればよいでしょうか?どうやって登録しましたか?
問題のプロジェクトは Pstmt Null Safe です。基本的に、Java クラスローダーには、jar をロードするときに META-INF というメタデータ フォルダーを検索するという魔法があります。 JDBC ドライバーの場合は、META-INF/services/java.sql.Driver であり、java.sql.Driver を実装するクラスでそれをメモしました: br.com.softsite.pstmtnullsafe.jdbc.PstmtNullSafeDriver.
java.sql.Driver のドキュメントによると、すべてのドライバーはそれ自体のインスタンスを作成し、DriverManager に登録する必要があります。私は次のように実装しました:
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
静的ブロックはそれ自体をロードします。そして、どの接続をドライバーが管理すべきかをどうやって知ることができるのでしょうか?呼び出しは DriverManager#getConnection(String url) を通じて行われます。ドライバーに接続を受け入れるかどうかを問い合わせるための URL があります。慣例 (ここでも慣用的な使用方法) は、URL スキームの先頭にそれを付けることです。自分のドライバーを別のドライバーの上に接続したいので、次のスキームを使用して接続しました。
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
そこで、テストを実行するために SQLite に接続し、Xerial インジケーターを使用して接続 URI を通じてメモリ内接続をリクエストしました。
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
接続を「エンベロープ」するには、私の規則では jdbc: を繰り返さないことが示されているため、次のようになります。
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
上記の URI を分析します:
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
それで、これをどのように示しますか?接続を開くことができる場合、Driver#acceptsURL は true を返す必要があります。これだけでできます:
public static final PstmtNullSafeDriver instance; static { instance = new PstmtNullSafeDriver(); try { DriverManager.registerDriver(instance); } catch (SQLException e) { e.printStackTrace(); } }
しかし、存在しないドライバーをロードしようとした場合、これは何を示すのでしょうか?何もありません。別の機会に問題が発生する可能性があります。それは良くない、理想は最初からクラッシュすることだ。このため、以下からドライバーをロードしようとします。ロードできない場合は false を返します:
jdbc:pstmt-nullsafe:<url de conexão sem jdbc:> \__/ \____________/ | | | Nome do meu driver Padrão para indicar JDBC
実際のドライバー コードには、HikariCP、DataSource、JDBC、またはこの投稿で取り上げるトピックについてのここでの議論には関係のない点がさらにいくつかあります。
そのため、DriverManager への「ヌル セーフ」接続をリクエストすると、まずドライバーが検索され、ドライバーは内部で接続の可能性があるかどうかを再帰的に確認しようとします。これに対処できるドライバーがあることを確認したので、可能です。
Connection インターフェイスは AutoCloseable インターフェイスを実装します。これは、接続を取得し、必要に応じてその接続を使用し、その後接続を閉じることを意味します。これで間接的に使用するか、接続を直接使用する場合は try-with-resources:
ブロック内で使用するのが非常に標準的です。
jdbc:sqlite::memory:
ところで、つながりを築くプロセスは高価なプロセスです。また、サービス検出 プロセスも完全に無料というわけではありません。したがって、ドライバー を保存してから接続を生成するのが理想的です。これを少しずつ開発していきましょう。
まず、ドライバーで起動できるオブジェクトを用意する必要があります。グローバル オブジェクト、挿入された Spring コンポーネント、またはその類のものを簡単に使用できます。これを JdbcConnector と呼びましょう:
jdbc:pstmt-nullsafe:sqlite::memory:
getJdbcConnection() の実装可能な 1 つは、この関数に含まれる状態に依存することです。
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
今のところすべてが順調です。しかし...農民が道具プールで鍬を求めた最初の例を覚えていますか?ということで…これを考慮してみましょうか?実際に接続を閉じる代わりに、接続をプールに戻すことができます。正確を期すために、複数の同時アクセスから保護しますが、ここでは効率については心配しません。
ここでは、ConnectionDelegator というクラスがあると仮定しましょう。これはすべての Connection メソッドを実装しますが、それ自体では何も行わず、コンストラクターとして渡された接続に委任するだけです。たとえば、isClosed():
メソッドの場合
try (final var pstmt = conn.prepareStatement( """ INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) """)) { pstmt.setInt(1, 1); pstmt.setString(2, "first"); pstmt.setNull(3, Types.INTGEGER); // java.sql.Types pstmt.executeUpdate(); // de fato altere o valor }
他の方法についても同様です。これを使用するときに、単純な委任以外の何かを自分に強制したいという単純な事実に対して抽象的です。
それでは、行きましょう。考え方としては、接続が要求されるが、存在する場合もあれば、存在しない場合もあります。存在する場合は、この新しいクラスでラップして、接続を閉じるときに pool に返せるようにします。うーん、それでは close() メソッドで何かをするつもりです... さて、最初にラップしましょう。同時実行の問題を避けるために、getConnection() を同期したままにしておきます。
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?)
わかりました。接続の プール に要素がある場合は、それが空になるまで使用します。しかし、それは決して満たされません!それで、この問題は解決するのでしょうか?閉まったらプールに返却しましょう!
INSERT INTO some_table (id, content, parent) VALUES (?, ?, ?) -- 1, 'first', NULL
OK、接続の使用が終了すると、
に送信されます。
プール。これは、Connection#close() メソッドのドキュメントの内容を満たしていません。ドキュメントには、この接続に関連するすべての JDBC リソースが解放されると記載されているからです。これは、すべての Statement、ResultSet、PreparedStatement などの記録を保持する必要があることを意味します。これは、ConnectionDelegator に closeAllInnerResources() という保護されたメソッドを作成することで処理できます。そしてそれを close() で呼び出します:
-- (value, value, NULL) INSERT INTO some_table (id, content, parent) VALUES (?, ?, NULL) -- 1, 'first'
これにより、オンデマンドで私に接続を返し、リソースのプールを形成する機能を備えたものができました。
Java が接続を提供するオブジェクトにどのような名前を付けるか知っていますか?データソース。 Java が DataSource について他に何を言っているか知っていますか?概念的に言えば、いくつかのタイプがあるということ。これらのタイプのうち、最も関連性の高い 2 つは次のとおりです:
そしてここでは、常に接続 (基本タイプ) を作成し、データソースに進化するプロセスを実行します
プール.
HikariCP はデータソースです。具体的には、プールされた DataSource。しかし、彼には 1 つの特徴があります。それは、彼が誰よりも速いということです。この速度を保証するために、アプリケーションのライフサイクル中に使用する接続のプールで、HikariCP は秘密を作ります。つまり、使用可能なすべての接続がすでに作成されています。したがって、getConnection が到着すると、HikariCP は接続のプールをチェックするだけで済みます。
このテーマをさらに詳しく知りたい場合は、このテーマに関する Baeldung のこの記事をチェックしてください。また、github のリポジトリもチェックしてください。
以上が光プールとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。