Где я должен помещать аннотацию @Transactional: при определении интерфейса или в классе реализации?

Вопрос из названия в коде:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

против

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

Ответ 1

От http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Рекомендация команды Spring заключается в том, что вы только аннотируете конкретные классы с аннотацией @Transactional, в отличие от аннотирования интерфейсов.. Вы, безусловно, можете разместить аннотацию @Transactional на интерфейсе (или метод интерфейса), но это будет работать только так, как вы ожидаете, если вы используете прокси на основе интерфейса. Тот факт, что аннотации не наследуются, означает, что если вы используете прокси на основе классов, то параметры транзакции не будут распознаваться инфраструктурой проксирования на основе классов, и объект не будет завернут в транзакционный прокси (что было бы явно плохо), Поэтому, пожалуйста, возьмите совет команды Spring и только аннотируйте конкретные классы (и методы конкретных классов) с аннотацией @Transactional.

Примечание. Поскольку этот механизм основан на прокси-серверах, будут перехвачены только "внешние" вызовы методов, поступающие через прокси-сервер. Это означает, что "self-invocation", то есть метод внутри целевого объекта, вызывающий какой-либо другой метод целевого объекта, не приведет к фактической транзакции во время выполнения, даже если вызываемый метод помечен @Transactional!

(Акцент добавлен к первому предложению, другой акцент сделан на оригинале.)

Ответ 2

Вы можете поместить их в интерфейс, но предупреждайте, что в некоторых случаях транзакции могут не закончиться. См. Второй отзыв в Secion 10.5.6 Spring docs:

Spring рекомендует только аннотировать конкретные классы (и методы конкретных классов) с аннотацией @Transactional, а не аннотировать интерфейсы. Вы, конечно, можете поместить аннотацию @Transactional на интерфейс (или метод интерфейса), но это работает только так, как вы ожидали бы, если вы используете прокси на основе интерфейса. Тот факт, что аннотации Java не унаследованы от интерфейсов, означает, что если вы используете прокси-классы на основе класса (proxy-target- class= "true" ) или аспект на основе ткачества (mode = "aspectj" ), то настройки транзакции не распознаются инфраструктурой проксирования и ткачества, и объект не будет обернут в транзакционный прокси, что будет явно плохо.

Я бы порекомендовал поставить их на реализацию по этой причине.

Кроме того, для меня транзакции выглядят как детали реализации, поэтому они должны быть в классе реализации. Представьте, что у вас есть реализации оболочки для ведения журналов или тестовых реализаций (mocks), которые не должны быть транзакционными.

Ответ 3

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

Если вы отметили что-то транзакционное в интерфейсе, а затем обратитесь к одному из своих классов реализации в другом месте в spring, не совсем очевидно, что объект, созданный spring, не будет уважать аннотацию @Transactional.

На практике это выглядит примерно так:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

Ответ 4

Поддержка @Transactional для конкретных классов:

Я предпочитаю архитектор решения в трех разделах: API, Реализация и Веб (при необходимости). Я стараюсь максимально упростить API как легкий/простой/POJO, минимизируя зависимости. Это особенно важно, если вы играете в распределенной/интегрированной среде, где вам нужно много разделить API.

Для размещения @Transactional в разделе API требуются библиотеки Spring, которые IMHO неэффективны. Поэтому я предпочитаю добавлять его в Реализация, где выполняется транзакция.

Ответ 5

Помещая это на интерфейс, прекрасно, поскольку все ожидаемые исполнители вашей МФК заботятся о данных TX (транзакции - это не проблемы, с которыми сталкиваются только базы данных). Если метод не заботится о TX (но вам нужно поместить его туда для Hibernate или что-то еще), поместите его в impl.

Кроме того, может быть немного лучше разместить @Transactional для методов в интерфейсе:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}