Запуск новой транзакции в Spring bean - PullRequest
42 голосов
/ 14 июня 2010

У нас есть:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...

MyInterface имеет единственный метод: go().

Когда выполняется go (), мы запускаем новую транзакцию, которая фиксирует / откатывает, когда метод завершен - это нормально.

Теперь, скажем, в go () мы вызываем закрытый метод в MyClassэто имеет @Transactional(propagation = Propagation.REQUIRES_NEW.Кажется, что Spring «игнорирует» аннотацию REQUIRES_NEW и не запускает новую транзакцию.Я считаю, что это потому, что Spring AOP работает на уровне интерфейса (MyInterface) и не перехватывает любые вызовы методов MyClass.Это правильно?

Есть ли способ начать новую транзакцию внутри транзакции go ()?Является ли единственный способ вызвать другой управляемый bean-компонент Spring, для которого транзакции настроены как REQUIRES_NEW?


Обновление : добавление того, что при выполнении клиентами go() онипоэтому через ссылку на интерфейс, а не на класс:

@Autowired
MyInterface impl;

impl.go();

Ответы [ 3 ]

75 голосов
/ 14 июня 2010

Из ссылки на Spring 2.5:

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

Поэтому Spring игнорирует @Transactional аннотация к закрытым методам.

Кроме того,

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

Таким образом, даже если вы сделаете свой метод public, вызов его из метода того же класса не запустит новую транзакцию.

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

Подробнее см. справочный документ .

Другой возможный способ сделать этовыбирает весенний прокси класса в самом классе и вызывает для него методы, а не this:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class SomeService {

    @Autowired
    private ApplicationContext applicationContext;

    private SomeService  getSpringProxy() {
        return applicationContext.getBean(this.getClass());
    }

    private void doSomeAndThenMore() {
        // instead of
        // this.doSometingPublicly();
        // do the following to run in transaction
        getSpringProxy().doSometingPublicly();
    }

    public void doSometingPublicly() {
        //do some transactional stuff here
    }

}
42 голосов
/ 14 июня 2010

@Transactional будет замечено только при использовании метода public из-за способа работы Spring AOP.

Однако вы можете программно начать новую транзакцию , если выжелание, используя TransactionTemplate, например

TransactionTemplate txTemplate = new TransactionTemplate(txManager);                
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallback<Object>() {
    public Object doInTransaction(TransactionStatus status) {
        // do stuff
    }
});
5 голосов
/ 14 ноября 2016

Короче говоря, вы должны вызывать метод через прокси для достижения транзакционного поведения. Можно вызывать «REQUIRES_NEW» в том же компоненте, как указано в вопросе. Чтобы сделать это, вы должны сделать ссылку на себя. Весной это не просто возможно. Вы должны ввести его с аннотацией @Resource.

@Service("someService")
public class ServieImpl implements Service {

   @Resource(name = "someService")
   Service selfReference;

   @Transactional
   public void firstMethod() {
       selfReference.secondMethod();
   }

   @Transactional(propagation = Propagation.REQUIRES_NEW) 
   public void secondMethod() {    
         //do in new transaction
   }

} 

Вызов в firstMethod вызывает прокси, а не «this», что должно заставить транзакцию «REQUIRES_NEW» работать.

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