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();
        }
    }
}

可以添加各种看起来合理的钩子。通用版本将允许各种返回类型,这可能更合适,这取决于您实际需要什么。

评论提到RunnableCallableIMORunnable专门用于线程(并且基本接口不是通用的)。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

那么为什么不做这样的事情:

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);
        }
    }
}

这样,您所有与事务相关的代码都在一个地方,每个单独的方法只负责格式化相应的语句。

于 2012-01-11T14:45:51.363 回答
0

更业余的方法是创建一个像 executeSQL 这样的方法来处理常见的东西,你只需传递 sql (String) 来执行。

更好的方法是查看 Spring 并让 Spring 为您处理事务和异常处理。

于 2012-01-11T14:46:38.103 回答
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 回答