Работает ли атрибут Spring @Transactional с закрытым методом? - PullRequest
174 голосов
/ 09 декабря 2010

Если у меня есть @ Transactional -аннотация для закрытого метода в бине Spring, оказывает ли эта аннотация какой-либо эффект?

Если аннотация @Transactional для открытого метода, она работает и открывает транзакцию.

public class Bean {
  public void doStuff() {
     doPrivateStuff();
  }
  @Transactional
  private void doPrivateStuff() {

  }
}

...

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

Ответы [ 8 ]

206 голосов
/ 09 декабря 2010

Ответ на ваш вопрос - нет - @Transactional не будет иметь эффекта, если будет использоваться для аннотирования частных методов.Прокси-генератор игнорирует их.

Это задокументировано в Руководство по пружине, глава 10.5.6 :

Видимость метода и @Transactional

При использовании прокси вы должны применять аннотацию @Transactional только к методам с публичной видимостью.Если вы аннотируете защищенные, частные или видимые в пакете методы с помощью аннотации @Transactional, ошибка не возникает, но аннотированный метод не отображает настроенные параметры транзакции.Рассмотрите возможность использования AspectJ (см. Ниже), если вам нужно аннотировать закрытые методы.

134 голосов
/ 09 декабря 2010

Вопрос не является частным или публичным, вопрос заключается в следующем: как он вызывается и какую реализацию AOP вы используете!

Если вы используете (по умолчанию) Spring Proxy AOP, то все функциональные возможности AOP, предоставляемые Spring (например, @Transational), будут приняты во внимание, только если вызов проходит через прокси. - Обычно это так, если аннотированный метод вызывается из другого компонента.

Это имеет два значения:

  • Поскольку частные методы нельзя вызывать из другого компонента (исключение составляет отражение), их @Transactional Аннотация не учитывается.
  • Если метод общедоступный, но вызывается из того же компонента, он также не будет учитываться (это утверждение верно только в том случае, если используется (по умолчанию) Spring Proxy AOP).

@ См. Ссылка на пружину: Глава 9.6 9.6 Механизмы прокси

ИМХО, вы должны использовать режим aspectJ вместо Spring Proxies, который решит проблему. А транзакционные аспекты AspectJ вплетены даже в приватные методы (проверено на Spring 3.0).

28 голосов
/ 09 декабря 2010

По умолчанию атрибут @Transactional работает только при вызове аннотированного метода для ссылки, полученной из applicationContext.

public class Bean {
  public void doStuff() {
    doTransactionStuff();
  }
  @Transactional
  public void doTransactionStuff() {

  }
}

Это откроет транзакцию:

Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();

Это будетnot:

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

Ссылка на пружину: использование @ Transactional

Примечание: в режиме прокси (который используется по умолчанию) только внешние вызовы методавход через прокси будет перехвачен.Это означает, что «самовывоз», то есть метод в целевом объекте, вызывающий какой-либо другой метод целевого объекта, не приведет к реальной транзакции во время выполнения, даже если вызванный метод помечен @Transactional!

Подумайте об использовании режима AspectJ (см. Ниже), если вы ожидаете, что самовыводы также будут обернуты транзакциями.В этом случае, во-первых, не будет прокси;вместо этого целевой класс будет «соткан» (т. е. его байт-код будет изменен), чтобы превратить @Transactional в поведение во время выполнения любого метода.

11 голосов
/ 28 февраля 2016

Да, можно использовать @Transactional для частных методов, но, как уже упоминали другие, это не будет работать из коробки.Вам нужно использовать AspectJ.Мне потребовалось некоторое время, чтобы понять, как заставить это работать.Я поделюсь своими результатами.

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

Сначала добавьте зависимость для aspectjrt.

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.8</version>
</dependency>

Затем добавьте плагин AspectJ для создания действительного байт-кода.ткачество в Maven (это может быть не минимальный пример).

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.8</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Наконец добавьте это в свой класс конфигурации

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)

Теперь вы сможете использовать @Transactional для частных методов..

Одно предостережение об этом подходе: вам нужно будет сконфигурировать свою среду IDE, чтобы иметь представление об AspectJ, иначе, если вы запустите приложение, например, через Eclipse, оно может не работать.Удостоверьтесь, что вы проверяете против прямой сборки Maven как проверку работоспособности.

3 голосов
/ 25 октября 2018

Если вам нужно обернуть закрытый метод внутри транзакции и не хотите использовать aspectj, вы можете использовать TransactionTemplate .

@Service
public class MyService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    private void process(){
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                processInTransaction();
            }
        });

    }

    private void processInTransaction(){
        //...
    }

}
3 голосов
/ 09 декабря 2010

Spring Docs объясняет, что

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

Подумайте об использовании режима AspectJ (см. Атрибут mode в таблице ниже), если вы ожидаете, что самовыводы также будут обернуты транзакциями.В этом случае, во-первых, не будет прокси;вместо этого целевой класс будет соткан (то есть его байт-код будет изменен), чтобы превратить @Transactional в поведение во время выполнения для любого метода.

Другой способ - пользователь BeanSelfAware

3 голосов
/ 09 декабря 2010

Ответ - нет. Пожалуйста, смотрите Spring Reference: Использование @ Transactional :

Аннотация @Transactional может быть помещена перед определением интерфейса, методом интерфейса, определением класса или методом public для класса

0 голосов
/ 06 мая 2019

Так же, как @ loonis предложил для использования TransactionTemplate можно использовать этот вспомогательный компонент (Kotlin):

@Component
class TransactionalUtils {
    /**
     * Execute any [block] of code (even private methods)
     * as if it was effectively [Transactional]
     */
    @Transactional
    fun <R> executeAsTransactional(block: () -> R): R {
        return block()
    }
}

Использование:

@Service
class SomeService(private val transactionalUtils: TransactionalUtils) {

    fun foo() {
        transactionalUtils.executeAsTransactional { transactionalFoo() }
    }

    private fun transactionalFoo() {
        println("This method is executed within transaction")
    }
}

Не знаю, будет ли TransactionTemplate повторно использовать существующую транзакцию или нет, но этот код определенно делает.

...