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


11

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

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

フィードバックを歓迎します、ありがとう。


どのような問題に言及していますか?誰がこれらの疑問を持っていますか?(あなたではない、私は推測します。)
グレッグヒューギル

1
開発者が接続を閉じるのを忘れるなどの問題は、一般に、データベース接続をパラメーターとしてさまざまなメソッドに渡すのが良い習慣かどうかを確認しようとしています。疑いは別の開発者から来ています。
ipohfly

回答:


8

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

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

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


これが答えであるとマークすることで、状況がどのようになっているかについてより多くのアイデアを得ることができます
-ipohfly

6

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

DIを使用することで、接続の作成、オープン、使用、およびクローズに関するロジックを、コアビジネスロジックから切り離すことができます。

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


Emmは依存性注入を実際には見ていません。一般的にそれを行うのが良いプラクティスであるかどうかを確認しようとします。そうでない場合、データベース接続を管理するより良い方法は何ですか(DIはそれを行う1つの方法です)。
ipohfly

-1。1つの接続はマルチユーザーシステムに適合しません。ユーザーのボリュームが少なく、実行速度が速いため、動作しているように見える場合があります。プーリングを使用する場合は、単一のユーザーシステムでもアクションごとに接続オブジェクトをインスタンス化することをお勧めします。
mike30

2

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

データベースのことをやり取りする際の問題は、それがずさんなことがあるということです。データベース接続を介して渡される誰かのコードに複数のバグがあり、誰かがローカルオブジェクト(結果セット、まだデータベースに接続されている)に結果セットを取得して隠し、次にカーソルをかなりの時間データベース。別のインスタンスが結果セットを他の誰かに渡して(それが隠された)、結果セットを渡したメソッドがそれ(およびステートメント)を閉じて、他のメソッドがそれ以外の結果セットを処理しようとしたときにエラーが発生しました。

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

これを回避するために、既存のパターンと構造があり、データベースとよりうまく連携し、制限されているクラスから抜け出すために必要なデータベースがありません。


1
+1 db接続には、可能な限り最短の時間間隔が必要です。開いて、使用して、できるだけ早く閉じます。最近では、接続プールの実装が多数あるため、複数の操作に接続を使用すると、誤った経済になります。バグまたはパフォーマンスの問題への招待(テーブルのロックの保持、接続リソースの使用)
jqa

これらの既存のパターンおよび構造のいくつかの名前は何ですか?
ダニエルカプラン

@tieTYT最前線に来る主なものは、アプリケーションの残りの部分からデータベースを隠すのに役立つデータアクセスオブジェクトです。 データアクセスレイヤーオブジェクトリレーショナルマッピング

これらのパターンについて考えるとき、彼が求めているものよりも抽象化のレベルが高いと感じます。RootDaoからを取得する方法があるとしましょう。しかし、その後NodeRootオブジェクト全体を引き出しずに取得する方法も必要であることに気付きます。RootDaoがNodeDaoコードを呼び出す方法(再利用)をどのように設定しますがNodeNodeDaoが直接呼び出されたときにのみ接続を閉じ、RootDaoが呼び出されたときに接続を開いたままにしますか?
ダニエルカプラン

1
自動コミットモードではない場合、接続を渡すと、あるオブジェクトがデータベースを更新し、別の(おそらく無関係の)オブジェクトが接続を取得し、エラーが発生して終了するという状況につながる可能性があることを追加したいだけです最初のオブジェクトの変更をロールバックします。これらのタイプのエラーは、デバッグが非常に難しい場合があります。
TMN

2

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
}

1
似たようなものを使用します。関数doInTransaction(DbTask task)があります。DbTaskは、接続パラメーターを持つメソッドとのインターフェイスです。doInTransactionは接続を取得し、タスクを呼び出してコミット(または例外が発生した場合はロールバック)し、その接続を閉じます。
user470365

あなたの例から判断すると、それはDataSourceオブジェクトがシングルトンであることを意味しますか?
ipohfly

@ipohfly実は私はそのオブジェクトを命名している必要がありますdataSourceではなく、DataSource(私はその点についての私の答えを修正します)。そのオブジェクトの正確なタイプはになりますjavax.sql.DataSource。古いコードでは、アプリケーション内で利用可能なすべてのデータソースをシングルトンで管理していました。DataSourceインスタンスは依存性注入によって提供されるため、私のDAOはそれについて知る必要がありませんでした。
ケビンLH

このスキーマを使用する場合は、try-with-resourcesをより適切に使用してください
-user1156544

私が答えたとき、私はまだJava 7を使用していませんでした。しかし、あなたはこれが最近の好ましい方法であるべきであることは正しいです。回答を更新して、提案を含めます。
-KevinLH

0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.