3

私が継承したコードには、次の形式の多くのトランザクション コード メソッドがあります。

public void addCourseToCourses(String values)
{
    try
    {
        conn.setAutoCommit(false);
    }
    catch (SQLException e)
    {
        Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e);
        return;
    }
    try
    {
        stmt.executeUpdate("insert into courses values " + values);
        conn.commit();
    }
    catch (SQLException e)
    {
        try
        {
            conn.rollback();
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
    finally
    {
        try
        {
            conn.setAutoCommit(true);
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
}

メソッドごとに異なる変数部分はどこですか

stmt.executeUpdate("insert into courses values " + values);

いくつかの挿入の場合もあれば、削除の場合もあります。C++ からのマクロの使用を見逃していますが、メソッドごとにこのすべてのトランザクション コードを繰り返さない方法があると確信しています。

ヘルプ ?

(conn および stmt は、タイプ java.sql.Connection および java.sql.Statement のクラス メンバーです)

4

6 に答える 6

6

インターフェイスを受け取り、例外処理をラップするメソッドを作成します。

各インターフェースの匿名 (または非匿名) 実装には、SQL 呼び出し、そのパラメーターなどが含まれます。

例(非常に大まかに):

public void addCourseToCourses(final String values) {
    handleSql(new SqlCommand() {
        @Override public void run(Statement stmt) {
            stmt.executeUpdate("insert into courses values " + values);
        }
    });
}

これhandleSqlは、次のようなものを静的にインポートしたものです。

public class SqlWrapper {
    public static void handleSql(SqlCommand cmd) {
        Connection conn = // get connection;
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
            LOG.log(Level.SEVERE, null, e);
            return;
        }

        try {
            cmd.run();
            conn.commit();
        } catch (SQLException e) {
            cleanRollback();
        } finally {
            cleanClose();
        }
    }
}

合理的に思えるように、さまざまなフックを追加できます。汎用バージョンでは、実際に必要なものに応じて、より適切なさまざまな戻り値の型が許可されます。

コメントは and に言及してRunnableおりCallable、IMORunnableはスレッド専用です (基本インターフェイスは一般的ではありません)。Callableより良い選択ですが、アプリ固有のものを使用するSQL/JDBC固有の機能を処理するために、他の十分なフックが追加されることを期待しています。YMMV。

このパターンはいたるところで再発明されています。Spring JDBC のようなものを取り、それをそのまま使用する方が理にかなっているかもしれません。

于 2012-01-11T14:42:56.137 に答える
2

実際、新しいインターフェースは必要ありません。Runnable や Callable などの既存のもので問題ありません。

于 2012-01-11T14:47:32.610 に答える
1

次のように、Java で匿名クラスを使用して実現している「コールバック」パターンを使用します。

実際の作業を行うためのインターフェースを作成します。

interface Exec {
    exec(PreparedStatement stmt) throws SQLException;
}

コードをリファクタリングして、Exec を受け入れる共通コードでメソッドを作成します。

void perform(Exec exec) {
    try
    {
        conn.setAutoCommit(false);
    }
    catch (SQLException e)
    {
        Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e);
        return;
    }
    try
    {
        exec.exec(stmt);
        conn.commit();
    }
    catch (SQLException e)
    {
        try
        {
            conn.rollback();
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
    finally
    {
        try
        {
            conn.setAutoCommit(true);
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
}

次のようにメソッドをリファクタリングします。

public void addCourseToCourses(final String values)
{
    perform(new Exec() {
        exec(PreparedStatement stmt) throws SQLException {
            stmt.executeUpdate("insert into courses values " + values);
        }
    });
}
于 2012-01-11T14:49:31.590 に答える
0

SQLステートメントのリストを使用します。

function void executeTransaction(List<String> sqlStatements) {
    // init transaction (as before)
    try
    {
        conn.setAutoCommit(false);
    }
    catch (SQLException e)
    {
        Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e);
        return;
    }
    try
    {
        // execute all statements from transaction
        for(String statement: sqlStatements) {
           stmt.executeUpdate("insert into courses values " + values);
        }
        // Commit when all succeed
        conn.commit();
    }
    catch (SQLException e)
    {
        try
        {
            // rollback on failure (as before)
            conn.rollback();
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
    finally
    {
        try
        {
            // cleanup (as before)
            conn.setAutoCommit(true);
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
}
于 2012-01-11T14:56:10.973 に答える
0

よりアマチュア的なアプローチは、一般的なものを処理する executeSQL のようなメソッドを作成し、単純に sql (文字列) を渡して実行することです。

より良いアプローチは、Spring を見て、Spring にトランザクションと例外処理を処理させることです。

于 2012-01-11T14:46:38.103 に答える
0

それでは、次のようなことをしてみませんか:

public void addCourseToCourses(String values)
{
    callDB("insert into courses values " + values)
}

protected void callDB(String call)
{
    try
    {
        conn.setAutoCommit(false);
    }
    catch (SQLException e)
    {
        Logger.getLogger(connDb.class.getName()).log(Level.SEVERE, null, e);
        return;
    }
    try
    {
        stmt.executeUpdate(call);
        conn.commit();
    }
    catch (SQLException e)
    {
        try
        {
            conn.rollback();
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
    finally
    {
        try
        {
            conn.setAutoCommit(true);
        }
        catch (SQLException ex)
        {
            Logger.getLogger(connDb.class.getName()).log(Level.SEVERE,null, ex);
        }
    }
}

このように、すべてのトランザクション関連のコードが 1 か所にあり、個々のメソッドは対応するステートメントの書式設定のみを担当します。

于 2012-01-11T14:45:51.363 に答える