Как бы вы пошли на оптимизацию (без повторения кода) Java-код, который имеет много функций этого типа - PullRequest
3 голосов
/ 11 января 2012

Код, который я унаследовал, имеет много методов кода транзакции этой формы:

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 ++, но я уверен, что есть способ не повторять весь этот код транзакции для каждого метода.

help?

(conn и stmt являются членами классов типовjava.sql.Connection и java.sql.Statement)

Ответы [ 6 ]

6 голосов
/ 11 января 2012

Создайте метод, который принимает интерфейс и завершает обработку исключений.

Каждая анонимная (или нет) реализация каждого интерфейса содержит вызов 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();
        }
    }
}

Могут быть добавлены различные крючки, которые кажутся разумными.Универсальная версия допускает различные типы возвращаемых данных, что, вероятно, более уместно, зависит только от того, что вам действительно нужно.

В комментарии упоминаются Runnable и Callable, IMO Runnable специально для потоков (и базовый интерфейс не является универсальным).Callable - лучший выбор, но я ожидаю, что будет добавлено достаточно других хуков для обработки специфичных для SQL / JDBC функций, которые я бы использовал для чего-то специфичного для приложения.YMMV.

Эта модель была заново изобретена повсюду;может иметь смысл взять что-то вроде Spring JDBC и просто использовать его.

2 голосов
/ 11 января 2012

На самом деле, вам не нужен новый интерфейс. Существующие, такие как Runnable или Callable, подойдут.

1 голос
/ 11 января 2012

Вы используете шаблон "обратного вызова", который в 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);
        }
    });
}
0 голосов
/ 11 января 2012

Использовать список операторов 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);
        }
    }
}
0 голосов
/ 11 января 2012

Более любительским подходом было бы создать такой метод, как executeSQL, который будет обрабатывать общие вещи, и вы просто передаете sql (String) для выполнения.

Лучше было бы взглянуть на Spring и позволить Spring обрабатывать транзакции и обработку исключений для вас.

0 голосов
/ 11 января 2012

Почему бы тогда просто не сделать что-то вроде этого:

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

Таким образом, весь код, связанный с транзакциями, находится в одном месте, и каждый отдельный метод отвечает только за форматирование соответствующего оператора.

...