АОП, весна и объем транзакций - PullRequest
4 голосов
/ 15 июня 2009

представьте себе транзакционное, многопоточное 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.

Теперь вопрос: можно ли решить эту проблему, используя аспекты? Если это так, что может быть основным подходом? Благодарю.

Ответы [ 3 ]

2 голосов
/ 17 июня 2009

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

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

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


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

И, очевидно, это хак, и я бы не стал выступать в гринфилд-системе:).

1 голос
/ 16 июня 2009

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

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

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

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

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

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

0 голосов
/ 17 июня 2009

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

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

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

...