AOP, Spring и область видимости транзакций

представьте себе транзакционное, многопоточное Java-приложение, использующее spring, jdbc и aop с n классами в m пакетах, которые все участвуют в переходах с базой данных. Теперь предположим, что необходимо охватить произвольный набор классов в рамках одной транзакции. Кроме того, всегда есть один класс T в области видимости, который фиксирует транзакцию при вызове.

Позвольте мне привести пример для ясности: учитывая пакеты A, B, Z и классы A.Foo, B.Bar и Z.T. Вызываются следующие экземпляры соответствующих классов (возможно, разными вызывающими объектами с другими классами между ними): A.Foo, B.Bar, A.Foo, Z.T Транзакции будут зафиксированы только после вызова Z.T. Если приложение закрывается по какой-либо причине, транзакция никогда не будет зафиксирована, если не будет задействован Z.T.

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

Теперь вопрос: можно ли решить эту проблему с помощью аспектов? Если да, то каким может быть основной подход? Спасибо.


person Community    schedule 15.06.2009    source источник
comment
Можете ли вы уточнить, находитесь ли вы внутри веб-контейнера, и всегда ли ваши транзакции происходят как часть веб-запроса, и есть ли у вас несколько транзакций на запрос?   -  person skaffman    schedule 15.06.2009
comment
Транзакции не должны знать или заботиться о том, используете ли вы веб-интерфейс. Это уровень службы, который знает об единицах работы, и именно здесь объявляются транзакции.   -  person duffymo    schedule 16.06.2009
comment
Я не в веб-контейнере - приложение работает автономно. duffymo прав, веб-интерфейс не должен иметь значения в этом случае (если бы он существовал, это не изменило бы проблему, с которой я столкнулся)   -  person    schedule 16.06.2009
comment
Это является релевантным, если он может использовать Spring beans с областью действия запроса или сеанса для обработки своего локального tx-состояния. Но он не находится в веб-контейнере, поэтому у него нет этой возможности, которая могла бы хорошо решить эту проблему.   -  person skaffman    schedule 17.06.2009


Ответы (3)


Вам не нужна единая точка входа, но вам нужна возможность применить транзакционный перехватчик ко всем точкам входа, чтобы повторно входящие вызовы могли участвовать в одной транзакции. Предполагая, что вы можете это сделать, вы могли бы сделать это с помощью флага ThreadLocal и специальной реализации org.springframework.transaction.support.TransactionSynchronization.

Вы бы изменили Z.T, чтобы установить флаг ThreadLocal, когда продолжение фиксации безопасно. В вашей реализации TransactionSynchronization.beforeCommit(), которая вызывается из PlatformTransactionManager, вы можете проверить флаг и использовать его, чтобы определить, разрешить ли выполнение фиксации. Вы можете принудительно выполнить откат, бросив RuntimeException, если флаг отсутствует.

Одно предостережение заключается в том, что если у вас есть другие типы транзакций (которые не включают три описанных вами координирующих класса), вам необходимо убедиться, что они не откатываются случайно. Для этого вы можете пометить эту «специальную транзакцию» в A.Foo, B.Bar и Z.T с помощью другого флага ThreadLocal, а затем проверить этот флаг в предложении защиты в методе beforeCommit(), упомянутом выше. Псевдокод:


void beforeCommit() {
  if in special transaction
    if commit flag not set
       throw new RuntimeException("cancel transaction")
    end if
  end if
end

И, очевидно, это взлом, и я бы не стал защищать систему с нуля :).

person Community    schedule 17.06.2009
comment
Это отправная точка, с которой я могу работать. Спасибо за полезный ответ. - person ; 17.06.2009

Идиома Spring рекомендует иметь интерфейс службы, который знает о единицах работы, и интерфейс постоянства, который имеет дело с реляционными базами данных. Методы в интерфейсе службы должны соответствовать вашим вариантам использования. Реализация службы знает обо всех моделях и пакетах персистентности и классах, необходимых для достижения целей варианта использования.

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

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

«... разные вызывающие абоненты с другими классами между ними ...» - возможно, вам нужно объявить транзакции индивидуально для этих вызывающих абонентов.

Вы можете объявлять транзакции в конфигурации XML, используя аспекты, либо с помощью Spring AOP, либо AspectJ. Spring 2.5 и выше теперь дает вам возможность использовать аннотации, если вы предпочитаете их конфигурации XML.

Ваше описание меня ужасно сбивает с толку. Может быть, это одна из причин, по которой у вас тоже с этим проблемы. Я бы переосмыслил или уточнить.

person duffymo    schedule 16.06.2009
comment
Я имею дело с устаревшей внутренней структурой, поэтому рефакторинг под идиомы Spring в лучшем случае затруднен. Данное описание является общим, однако я не вижу путаницы. Три разных экземпляра a, b, z трех разных классов, распределенных по трем различным пакетам, могут быть вызваны из любого места в приложении и вместе образуют единую транзакцию. Транзакция фиксируется только в том случае, если вызывается третий класс из трех. Итак, вызывается a, вызывается b и вызывается c - ›commit. Вызывается B, вызывается a, не вызывается ни c, ни фиксация. Вызывается A, вызывается c, фиксируется. - person ; 16.06.2009
comment
Наследие? Весна не такая уж и старая. Похоже, люди, которые это начали, не знали Spring. Мне было трудно поверить в то, что рефакторинг с помощью Spring будет настолько сложным. Какой-то объект должен создавать те экземпляры, которые взаимодействуют. Владелец объектов также должен владеть сделкой. Где угодно внутри приложения - звучит как лажа. Извините, ничем не могу вам помочь. - person duffymo; 17.06.2009
comment
Я могу сказать следующее: фреймворк делает то, что должен делать, и в этом отношении он хорошо спроектирован. Можно было бы изменить его в сторону принятых идиом, но слишком много работы. - person ; 17.06.2009

С весенними транзакциями и aop вы можете это сделать, но это будет немного "взломать" ...

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

Теперь единственный способ сказать Spring об откате транзакции - это выбросить исключение через границу транзакции. Таким образом, что вам нужно сделать, если вы войдете в эту область Z, которая вызовет фиксацию, тогда вам нужно будет поместить что-то в локальный поток (также, возможно, через аспект), который этот "внутренний" аспект найдет и, таким образом, не выбросит исключение для отката транзакции. Если вы не введете Z, то локальный поток не получит флаг, и когда вы вернетесь через внутренний аспект, будет выброшено исключение для отката транзакции. Вам, вероятно, придется проглотить это исключение.

person Michael Wiles    schedule 17.06.2009