Java: вызов функции автоматически при выходе из области видимости (например, деструкторы c ++) - PullRequest
5 голосов
/ 27 октября 2011

Для подключения mysql у меня есть объект подключения и я использую механизм транзакций connection.startTransaction(), connection.commitTransaction(), connection.rollbackTransaction().

Для каждого startTransaction() всегда должен быть либо звонок на commitTransaction(), либо на rollbackTransaction(). Пропуск такого вызова или вызов обоих может сломать мою систему транзакций.

Поэтому я использую их следующим образом:

boolean i_am_in_a_transaction=true;
try {
    connection.startTransaction();
    ...
    i_am_in_a_transaction=false;
    connection.commitTransaction();
} finally {
    if(i_am_in_a_transaction) {
        connection.rollbackTransaction();
    }
}

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

В C ++ я использовал бы объект транзакции, который проверяет в своем деструкторе, была ли вызвана функция commit(), и который в противном случае вызывает rollback():

class Transaction {
    public:
        Transaction()
            :am_in_transaction(false) {
        }

        ~Transaction() {
            if(_am_in_transaction) {
                rollback();
            }
        }

        void startTransaction() {
            _am_in_transaction=true;
            ...start Transaction...
        }

        void commit() {
            _am_in_transaction=false;
            ...commit Transaction...
        }

        void rollback() {
            _am_in_transaction=false;
            ...rollback Transaction...
        }

    private:
        bool _am_in_transaction;
}

Таким образом, я реализовал логику в одном месте и могу использовать ее очень просто:

Transaction my_transaction;
my_transaction.startTransaction;
...
my_transaction.commit();

Этот код намного проще, чем приведенный выше Java-код с блоком try / finally.

Есть ли способ реализовать это поведение в Java, не выделяя логику вызывающей стороне и не заставляя его реализовать блок try / finally?

Мне поможет что-то вроде способа автоматического вызова функции при выходе из области.

Ответы [ 6 ]

5 голосов
/ 27 октября 2011

В Java нет деструкторов. Но есть несколько «стандартных» решений, которые могут вам помочь.

1.Используйте шаблон с именем «метод шаблона».

abstract class Base {
    public query() {
         openTransaction();
         doQuery();
         closeTransaction();
    }
    protected abstract doQuery();
}

Теперь вы должны реализовать doQuery() в каждом создаваемом вами подклассе и использовать его, вызывая query () из базового класса.

2.Используйте аспектно-ориентированное программирование

3.Используйте шаблон Decorator (Wrapper).

4.Используйте одну из популярных платформ ORM (Hibernate, iBatis и т. Д.), Которые решают все эти проблемы за вас и не имеют ничего общего с низкоуровневыми JDBC-компонентами.

5 голосов
/ 27 октября 2011

Что-то похоже (очень важно) на деструкторы в C ++ - это метод finalize().Разница в том, что нет никакой гарантии, когда сборщик мусора действительно вызовет его, поэтому не рекомендуется полагаться на него.

В противном случае лучшее, что вы можете сделать, это try/finally блоков.

4 голосов
/ 27 октября 2011

На ум приходит пара решений ...

Как указывает @Dark Falcon, это было бы хорошим вариантом для попытки с вызовом ресурсов , которая автоматически очистит ваши ресурсы в конце попытки. К сожалению, это доступно только в Java 7.

Java-классы определяют finalize() метод, который может вызываться, когда объект подвергается сборке мусора, но переопределение этого метода почти никогда не подходит.

Я думаю, что ваш единственный вариант, если вы зацепились за идею «выполнить код при возврате функции», - это использовать Аспектно-ориентированное программирование . Если вы читаете о некоторых пакетах, таких как AspectJ или изучаете использование AOP с Spring , вы можете выполнить некоторые действия по настройке, чтобы получить код для выполнения, когда функция возвращается, перехватывая вызов. Вот пример использования Spring AOP для выполнения другого кода при возврате вашей функции.

2 голосов
/ 27 октября 2011

Если обновление до Java 7 является опцией, есть новая try with resources, которая будет выполнять метод close в реализации Closable.

1 голос
/ 27 октября 2011

Я бы выбрал только один метод и сделал бы его только один раз.

public static void update(Connection connection, String updateSQL) {
    PreparedStatement update = null;
    try {
      try {
        connection.startTransaction();
        update = connection.prepareStatement(updateString);
        update.executeUpdate();
      } finally {
        connection.rollbackTransaction();
      }
    connection.commitTransaction();
    } finally {
        if(update != null) update.close();
    }
}

позже

update(connection, updateSQL1);
update(connection, updateSQL2);
// etc.
0 голосов
/ 06 ноября 2014

Я знаю, что это старый вопрос, но в интересах других:

Вы можете использовать анонимный внутренний класс, который реализует интерфейс для выполнения работы, аналогично тому, как работают механизмы сортировки Java Comparator и List. Это позволяет обрабатывать внутренний класс так, как если бы он был областью выполнения.

например. Изменение вашего исходного примера:

class Transaction
{
   boolean i_am_in_a_transaction=false;

   interface AutoRollback
   {
       void runQueries() throws Throwable;
   }

    void startTransaction() {
        i_am_in_a_transaction=true;
        ...start Transaction...
    }

    void commit() {
        i_am_in_a_transaction=false;
        ...commit Transaction...
    }

    void rollback() {            
        i_am_in_a_transaction=false;
        ...rollback Transaction...
    }

   public void execute(AutoRollback work)
   {
      try {
          work.runQueries();
      } catch ( Throwable t ) {
          rollback();
          throw t;
      }
   }
}

А затем пример того, как его использовать:

void test() throws WhateverException
{
    Transaction my_transaction;
    my_transaction.startTransaction();

    my_transaction.execute( new AutoRollback() { public void runQueries() throws Throwable {

     ... perform your queries: can be more than one, complex code, etc. ...
     ... local variables from the enclosing scope can be used as long as they are final... 

    }});

    my_transaction.commit();
}

Если у вас есть Java 8, это становится намного красивее с лямбдами, так как сохраняет синтаксис new AutoRollback.

Если у вас нет Java 8, а лишняя пунктуация по-прежнему вас беспокоит, вы должны иметь возможность использовать процессор аннотаций и внедрение кода, чтобы сделать его читаемым. Аннотация во время компиляции с цель, установленная в LOCAL_VARIABLE, - то, что вы хотите, затем примените ее к my_transaction.

... При условии, что на вашем рабочем месте допустимы процессоры аннотаций, такие как apt или pre-процессоры, и вы хотите проделать такую ​​большую работу для синтаксического сахара.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...