web-dev-qa-db-ja.com

データベース接続-それらをパラメーターとして渡す必要がありますか?

共通のメソッドを使用してデータベース接続を一度取得し、関連するクラス全体に渡して使用するシステムがあります。データベース接続をパラメーターとして別のクラスに渡すと問題が発生するのではないかと疑われるので、これが実際に実行可能かどうかを確認するためにここで確認しています。

永続化を行うためのいくつかのORMツールがあることは知っていますが、それについてはまだ説明できません。

どんなフィードバックでも歓迎します、ありがとう。

11
ipohfly

はい、接続を迂回しても安全です。接続は外部制御ブロックで処理します。危険なことは何もありません。

安全でないのは、接続が適切なタイミングで適切に破棄されることを保証しないコードを記述することです。リソースをクリーンアップするのを忘れることは、リソースを渡すこととは無関係です。どこにも渡さずにハングしている接続を残すコードを簡単に作成できます。

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つの関数でアクションのすべての順列を処理できます。各順列の外部制御ブロックの重複を排除します。

8
mike30

Dependency Injection を実行しているようです。つまり、プールされた接続は一度作成され、必要な場所に注入されます。確かに、メソッドパラメータを介して接続を渡すことは、依存性注入の1つの方法ですが、Guice、PicoContainer、SpringなどのIoCコンテナは、これを実行できる別の(より安全な)方法です。

DIを使用すると、コアビジネスロジックから離れて、接続の作成、オープン、使用、クローズに関するロジックを適切にラップできます。

Spring JDBCなどは、この種の動作を実行する他の例です

6
Martijn Verburg

データではなくデータベースのものを渡すと、問題が発生する可能性があります。その範囲で、実用的な場合は、適切なデータベースの衛生状態を保証できない限り、データベースを通過させないでください。

データベースの問題を回避する際の問題は、データベースがずさんになる可能性があることです。データベース接続を迂回しているコードに複数のバグがありました。誰かが結果セットを取得してローカルオブジェクト(データベースに接続されたままの結果セット)に格納し、カーソルを重要な時間のデータベース。別のインスタンスが結果セットを他の誰かに渡して(それから格納された)、結果セットを渡したメソッドがそれを閉じ(そしてステートメント)、他のメソッドがもう存在しない結果セットを操作しようとしたときにエラーが発生しました。

これらはすべて、データベース、接続、ステートメント、結果セット、およびそれらのライフサイクルを尊重しないことに起因しています。

これを回避するために、既存のパターンと構造があり、データベースでより適切に動作し、データベースが閉じ込められているクラスから抜け出す必要がないデータベースがあります。データが入り、データが出て、データベースはそのまま残ります。

2
user40980

Connectionインスタンスを渡すことは通常問題ではありませんが、ほとんどの状況ではDAO実装のみがそれらと関係があるはずです。これで、使用後に接続が閉じられないという問題があるため、実際には簡単に修正できます。Connectionオブジェクトは、開かれているのと同じレベルで、つまり同じメソッドで閉じる必要があります。私は次のコードパターンを個人的に使用しています。

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

このようにして、ブロック内で例外がスローされた場合でも、すべての接続が常に閉じられるようにします。私は実際にStatementResultSetインスタンスにまったく同じパターンを使用している限り、すべてが順調に進んでいます。

2018-03-29を編集:以下のコメントのuser1156544で示されているように、Java 7で始まる場合、try-with-resources構文の使用が推奨されます。これを使用すると、最初の回答で提供したコードパターンを次のように簡略化できます。

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}
2
KevinLH

必要に応じて取得できるシングルトンを使用するのではなく、この方法で物事を行うことにはトレードオフがあります。私は過去に両方の方法で物事を行いました。

一般に、データベース接続管理の結果について考える必要があります。これは、データベースクエリの使用と直交する場合とそうでない場合があります。たとえば、特定のアプリケーションインスタンスに対して1つのdb接続があり、使用されていないときに閉じられる場合、それは直交します。管理をシングルトンクラスに入れ、それを渡さないでください。これにより、必要に応じてdb接続を管理できます。たとえば、すべてのコミットで接続を閉じたい(そして次の呼び出しで再び開く)場合、このAPIを集中化できるため、シングルトンでこれを行うのが簡単です。

一方、特定の呼び出しが任意の接続を使用する必要がある可能性がある接続のプールを管理する必要があるとします。これは、たとえば、複数のサーバーに分散トランザクションを実行するときに発生する可能性があります。この場合、通常、シングルトンを使用するよりも、db接続オブジェクトを渡す方がはるかに優れています。通常これはまれなケースだと思いますが、必要なときにそれを行うことで問題はありません。

0
Chris Travers