4

私は、昔ながらのほとんどが JDBC のパターンでいくつかのサーブレットを作成しています。単一のトランザクションを共有したいオブジェクトがいくつかあることに気付きました。1 つの HTTP トランザクション = 1 つのデータベース トランザクションを強制したいと考えています。

ThreadLocal変数で接続を渡し、その接続の作成/コミット/ロールバックを処理するサーブレットフィルターを使用することで、これを行うことができると思います。

私が関与していないこれを行う既存のフレームワークはありますか、それともこれは2000年代後半の合理的な方法ですか?

4

5 に答える 5

4

Springトランザクション管理は、あなたが説明したことを正確に実行します。一見すると少し圧倒されるかもしれませんが、(最も単純な場合に)必要なのは次のとおりです。

org.springframework.jdbc.datasource.DataSourceTransactionManager org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy org.springframework.transaction.support.TransactionTemplate

既存のデータソースを接続してTransctionAwareDataSourceProxyでラップし、ラップされたデータソースを使用してDataSourceTransactionManagerを作成し、これらをServletContextに保持します。次に、トランザクションごとに、トランザクションマネージャーを渡すTransactionTemplateを作成し、execute(TransactionCallback)メソッドを呼び出してコードを実行します。例えば:

new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
    public void doInTransaction(TransactionStatus ts){
        // run your code here...use the dataSource to get a connection and run stuff
        Connection c = dataSourceProxy.getConnection();
        // to rollback ... throw a RuntimeException out of this method or call 
        st.setRollbackOnly();
    }
});

接続は、常に同じデータソースから接続を取得する限り、つまりラップされたものである限り、ローカルスレッドにバインドされ、同じトランザクションで同じ接続を取得します。

これは可能な限り最も単純なSpringトランザクションのセットアップであることに注意してください...必ずしも最良または推奨されるものではありません。Springリファレンスドキュメントを参照するか、Springの動作を読んでください。

...ですから、直接的な答えとしては、そうです、それは合理的なことだと思います。それは、SpringFrameworkが長い間行ってきたことです。

于 2009-06-11T18:09:54.693 に答える
1

現在、ほとんどの appServer は JTA (Java Transaction Api) をサポートしています。これは、複数のオープン/クローズ JDBC 接続にまたがるトランザクションです。「threadLocal」の処理を行い、J2EE に準拠しています。フィルターで次のように使用します。

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    UserTransaction transaction = null;
    try {
        transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
        transaction.begin();
        chain.doFilter(request, response);
        transaction.commit();
    } catch (final Exception errorInServlet) {
        try {
            transaction.rollback();
        } catch (final Exception rollbackFailed) {
            log("No ! Transaction failed !",rollbackFailed);
        }
        throw new ServletException(errorInServlet);
    }
}

app-server で、jndi 名で Datasource を宣言し、それをコードで使用して接続を取得します (cx.commit()、cx.rollback()、または cx.setAutocommit() を作成しないでください。干渉します)。 JTAで)。同じ HTTP トランザクションで接続を数回開いたり閉じたりすることができます。JTA が処理します。

public void doingDatabaseStuff() throws Exception {
    DataSource datasource = (DataSource)new InitialContext().lookup("/path/to/datasource");
    Connection connection = datasource.getConnection();
    try {
        // doing stuff
    } finally {
        connection.close();
    }
}
于 2009-06-11T21:20:18.277 に答える
0

「本物の」アプリサーバーに頼ることができず、Spring のそれほど軽量ではないことを避けたい場合は、フィルターを使用して接続を提供し、それをスレッドに保持し、リクエストの最後に閉じます。実用的で合理的な解決策。

get() 接続と setRollbackOnly() を許可する (本質的に静的な) アクセサー クラスが必要になります。

リクエストの終了時に、フィルターの観点から、必ず例外をキャッチし (ログに記録してロールバックのみに設定する必要があります)、コミット/ロールバックし、それに応じてトランザクションを閉じます。

ほとんどのアプリケーションと Web コンテナー (および JTA は通常同様の仮定を行います) では、要求は 1 つのスレッドによって処理され、要求中にレイヤー間で再利用するために 1 つのデータベース接続をスレッドに関連付けることは、適切なことです。

于 2009-06-12T09:57:34.697 に答える
0

フィルターでトランザクションを管理することは、独自のトランザクション管理を展開するための優れたアプローチです。

Java EE 仕様はトランザクション管理を提供し、Spring のような代替フレームワークは同様のサポートを提供します (ただし、これは推奨ではありません。Spring は必ずしもこれをうまく行うとは限りません)。

ただし、 を使用すると、ThreadLocal問題が発生する可能性があります。たとえば、リクエスト全体で単一のスレッドが使用されるという保証はなくConnection、グローバル変数を介して何でもアクセスでき、設定するグローバル状態に依存している場合はテストがより困難になる可能性があります。依存性注入コンテナーを使用して、Connectionオブジェクトを必要とするオブジェクトに明示的に渡すことを検討します。

于 2009-06-11T17:27:29.553 に答える
0

一般に、「上からのパラメータ化」を使用してオブジェクトを渡す方が適切ThreadLocalです。の場合ServletFilter、 の属性がServletRequest明らかな場所になります。Connection サーブレットに依存しないコードへのインターフェイスは、意味のあるコンテキストに抽出できます。

于 2009-06-11T17:23:07.177 に答える