データベース接続が共通メソッドを使用して一度取得され、使用される関連クラス全体に渡されるシステムがあります。データベース接続をパラメーターとして別のクラスに渡すと問題が発生するのではないかという疑念があるので、ここで実際に実行可能かどうかを確認していますが、それを行うためのより良いパターンはありますか?
永続化を行うためのORMツールがいくつかあることは知っていますが、それについてはまだ説明できません。
フィードバックを歓迎します、ありがとう。
データベース接続が共通メソッドを使用して一度取得され、使用される関連クラス全体に渡されるシステムがあります。データベース接続をパラメーターとして別のクラスに渡すと問題が発生するのではないかという疑念があるので、ここで実際に実行可能かどうかを確認していますが、それを行うためのより良いパターンはありますか?
永続化を行うためのORMツールがいくつかあることは知っていますが、それについてはまだ説明できません。
フィードバックを歓迎します、ありがとう。
回答:
はい、接続を迂回しても安全です。接続は外部の制御ブロックで処理します。安全でないものは何もありません。
安全でないのは、接続が適時に適切に破棄されることを保証しないコードを書くことです。リソースをクリーンアップするのを忘れることは、リソースを渡すこととは無関係です。接続をどこにも渡さずに接続を切断するコードを簡単に作成できます。
C ++では、スタックに割り当てるか、スマートポインターを使用すると、RAIIによって保護されます。C#では、すべての使い捨てオブジェクト(接続など)を「using」ブロックで宣言するという厳しいルールを作成します。Javaでtry-finallyロジックでクリーンアップします。これを確実にするために、すべてのデータレイヤーコードのコードレビューを行ってください。
最も一般的なユースケースは、多くの順列で組み合わせることができる複数の操作がある場合です。そして、これらの順列はそれぞれアトミックトランザクションである必要があります(すべて成功またはロールバック)。その後、トランザクション(および対応する接続)をすべてのメソッドに渡す必要があります。
アトミックトランザクションとしてさまざまな方法で組み合わせることができる多くのfoobar()アクションがあるとします。
//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
conn.Open();
using (IDbTransaction tran = conn.BeginTransaction())
{
try
{//inner foobar actions just do their thing. They don't need to clean up.
foobar1(tran);
foobar2(tran);
foobar3(tran);
tran.Commit();
}
catch (Exception ex)
{ tran.Rollback(); }
}
}//connection is returned to the pool automatically
ところで、可能な限り遅く接続を開き、できるだけ早くそれらを破棄する必要があります。接続をオブジェクトメンバとして扱い、それらを不要な状態として導入し、接続を必要以上に長く開いたままにしておくと、チームメイトが正しい可能性があります。しかし、接続またはトランザクションをパラメーターとして渡すという行為は、本質的に間違っているわけではありません。
ところで。言語のファーストクラス機能のサポートに応じて、foobar()アクションのリストを取得できます。したがって、1つの関数でアクションのすべての順列を処理できます。各順列の外部制御ブロックの重複を排除します。
Dependency Injectionの後のようです。つまり、プールされた接続は一度作成され、必要なときに挿入されます。確かに、メソッドパラメーターを介して接続を渡すことは、依存性注入の1つの方法ですが、Guice、PicoContainer、SpringなどのIoCコンテナーは、これを行うことができる別の(より安全な)方法です。
DIを使用することで、接続の作成、オープン、使用、およびクローズに関するロジックを、コアビジネスロジックから切り離すことができます。
Spring JDBCなどは、この種の動作を実行する他の例です
データではなくデータベースのものを渡すと、問題が発生する可能性があります。適切なデータベースの衛生状態を保証できる場合を除き、その限りでは、実用的な場合は常にデータベースを渡さないでください。
データベースのことをやり取りする際の問題は、それがずさんなことがあるということです。データベース接続を介して渡される誰かのコードに複数のバグがあり、誰かがローカルオブジェクト(結果セット、まだデータベースに接続されている)に結果セットを取得して隠し、次にカーソルをかなりの時間データベース。別のインスタンスが結果セットを他の誰かに渡して(それが隠された)、結果セットを渡したメソッドがそれ(およびステートメント)を閉じて、他のメソッドがそれ以外の結果セットを処理しようとしたときにエラーが発生しました。
これはすべて、データベース、接続、ステートメント、結果セット、およびそれらのライフサイクルを尊重しないことに起因しています。
これを回避するために、既存のパターンと構造があり、データベースとよりうまく連携し、制限されているクラスから抜け出すために必要なデータベースがありません。
Root
Daoからを取得する方法があるとしましょう。しかし、その後Node
、Root
オブジェクト全体を引き出しずに取得する方法も必要であることに気付きます。Root
DaoがNode
Daoコードを呼び出す方法(再利用)をどのように設定しますがNode
、Node
Daoが直接呼び出されたときにのみ接続を閉じ、Root
Daoが呼び出されたときに接続を開いたままにしますか?
Connection
インスタンスを渡すことは通常、問題ではありませんが、ほとんどの場合、DAO実装のみがそれらと関係があるはずです。さて、使用後に接続が閉じられないという問題があるので、実際に修正するのは簡単です。Connection
オブジェクトを開くのと同じレベル、つまり同じメソッドで閉じる必要があります。私は個人的に次のコードパターンを使用します。
final Connection cnx = dataSource.getConnection();
try {
// Operations using the instance
} finally {
cnx.close();
}
これにより、ブロック内で例外がスローされた場合でも、すべての接続が常に閉じられるようになります。私は実際の正確な同じパターンを使用している限り行くStatement
とResultSet
インスタンス、およびすべてのものは、これまで順風満帆となっています。
2018-03-29の編集:以下のコメントのuser1156544で示されているように、Java 7以降では、try-with-resourcesコンストラクトの使用が推奨されます。これを使用して、最初の回答で提供したコードパターンを次のように簡略化できます。
try (final Connection cnx = dataSource.getConnection()) {
// Operations using the instance
}
dataSource
ではなく、DataSource
(私はその点についての私の答えを修正します)。そのオブジェクトの正確なタイプはになりますjavax.sql.DataSource
。古いコードでは、アプリケーション内で利用可能なすべてのデータソースをシングルトンで管理していました。DataSource
インスタンスは依存性注入によって提供されるため、私のDAOはそれについて知る必要がありませんでした。
必要に応じて取得できるシングルトンを使用するのではなく、この方法で物事を行うことにはトレードオフがあります。私は過去に両方の方法で物事を行ってきました。
一般に、データベース接続管理の結果について考える必要がありますが、これはデータベースクエリの使用と直交する場合と直交しない場合があります。たとえば、特定のアプリケーションインスタンスに対して1つのdb接続があり、使用されていないときに閉じられる場合、それは直交します。経営陣をシングルトンクラスに入れ、それをあきらめないでください。これにより、必要に応じてdb接続を管理できます。たとえば、コミットごとに接続を閉じたい(そして次の呼び出しで再び開きたい)場合、このAPIは一元化できるため、シングルトンで行う方が簡単です。
一方、特定の呼び出しが任意の接続を使用する必要がある接続のプールを管理する必要があるとします。これは、たとえば、複数のサーバーに分散トランザクションを行うときに発生する可能性があります。この場合、シングルトンで作業するよりも、通常、db接続オブジェクトを渡す方がはるかに優れています。通常、これはまれなケースだと思いますが、必要なときにそれを行うことで問題はありません。