2009-06-15 12 views
4

Stellen Sie sich eine transaktionale, Multithread-Java-Anwendung mit Spring, JDBC und AOP mit n-Klassen in m-Pakete alle an Datenbanktransaktionen teilnehmen. Nehmen wir an, dass es notwendig ist, eine beliebige Menge von Klassen innerhalb einer Transaktion zu definieren. Darüber hinaus gibt es immer eine Klasse T innerhalb des Bereichs, der die Transaktion beim Aufruf festschreibt.AOP, Spring und Transaktion Scoping

Lassen Sie mich ein Beispiel für die Klarheit geben: Angesichts der Pakete A, B, Z und Klassen A.Foo, B.Bar und Z.T. Die folgenden Instanzen der jeweiligen Klassen werden aufgerufen (möglicherweise durch verschiedene Aufrufer mit anderen Klassen dazwischen): A.Foo, B.Bar, A.Foo, Z.T Die Transaktionen werden erst nach dem Aufruf von Z.T übergeben. Sollte die Anwendung aus irgendeinem Grund herunterfahren, wird die Transaktion niemals ausgeführt, es sei denn, Z.T beteiligt sich.

Instanzen können sich gegenseitig aufrufen, und wie bereits erwähnt, gibt es keinen gemeinsamen Einstiegspunkt, der alle Instanzen von einem einzigen Einstiegspunkt aus aufruft (wie eine Serviceschicht), was ein einfaches Ziel für das Transaktions-Tag des Frühlings wäre.

Jetzt die Frage: Kann dieses Problem mit Aspekten gelöst werden? Wenn ja, was könnte der grundlegende Ansatz sein? Danke.

+0

Können Sie klären, ob Sie sich in einem Webcontainer befinden und ob Ihre Transaktionen immer als Teil einer Webanfrage ausgeführt werden und ob Sie mehrere Transaktionen pro Anfrage haben? – skaffman

+0

Transaktionen sollten nicht wissen oder sich darum kümmern, ob Sie ein Web-Frontend verwenden oder nicht. Es ist die Serviceschicht, die über Arbeitseinheiten Bescheid weiß, und hier werden Transaktionen deklariert. – duffymo

+0

Ich bin nicht in einem Web-Container - die Anwendung läuft eigenständig. Duffymo ist richtig, das Web-Frontend sollte in diesem Fall keine Rolle spielen (wenn es existierte, würde es das Problem nicht verändern) –

Antwort

2

Sie benötigen keinen einzigen Einstiegspunkt, aber Sie müssen den Transaktionsabfanger auf alle Einstiegspunkte anwenden können, damit eintretende Anrufe an derselben Transaktion teilnehmen können. Angenommen, Sie können dies mit einem ThreadLocal-Flag und einer benutzerdefinierten org.springframework.transaction.support.TransactionSynchronization-Implementierung erreichen.

Sie würden Z.T ändern, um das ThreadLocal-Flag zu setzen, wenn ein Commit sicher ausgeführt werden kann. In Ihrer TransactionSynchronization.beforeCommit() Implementierung, die von aufgerufen wird, können Sie das Flag überprüfen und verwenden, um zu bestimmen, ob das Commit fortgesetzt werden soll. Sie können einen Rollback erzwingen, indem Sie RuntimeException werfen, wenn die Flagge nicht vorhanden ist.

Ein Nachteil wäre, wenn Sie andere Arten von Transaktionen haben (die nicht die drei koordinierenden Klassen beinhalten, die Sie beschrieben haben), müssen Sie sicherstellen, dass sie nicht unbeabsichtigt zurückgesetzt werden. Um dies zu tun, könnten Sie diese "spezielle Transaktion" in A.Foo, B.Bar und Z.T über ein anderes ThreadLocal-Flag markieren und dann dieses Flag in einer Guard-Klausel in der oben erwähnten Methode beforeCommit() überprüfen. Pseudo-Code:


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

Und, natürlich, das ist ein Hack, und ich würde nicht in dem grünen Wiese System befürworten tun :).

+0

Das ist ein Anfangspunkt, von dem ich arbeiten kann. Danke für die hilfreiche Antwort. –

1

Das Spring-Idiom würde eine Serviceschnittstelle empfehlen, die über Arbeitseinheiten und eine Persistenzschnittstelle verfügt, die sich mit relationalen Datenbanken befasst. Die Methoden in der Service-Schnittstelle sollten Ihren Anwendungsfällen genau zugeordnet werden. Die Serviceimplementierung kennt alle Modell- und Persistenzpakete und -klassen, die sie benötigt, um die Ziele des Anwendungsfalls zu erreichen.

"Instanzen können sich gegenseitig aufrufen, und wie bereits erwähnt, gibt es keinen gemeinsamen Einstiegspunkt, der alle Instanzen von einem einzigen Einstiegspunkt aus aufruft (wie eine Serviceschicht), was ein einfaches Ziel für das Transaktions-Tag des Frühlings wäre."

Dieser Satz sagt mir, dass Sie Dinge auf eine Art und Weise tun, die sich nicht so leicht zu Spring's Idiom eignet. Es ist schwer zu sagen, was genau du willst, aber es klingt, als würdest du zwei der wichtigsten Schichten wegwerfen, die Spring empfiehlt. Wenn es schwierig scheint, gegen den Strich zu gehen, ist es vielleicht Ihr Design, das überarbeitet werden muss.

"... verschiedene Anrufer mit anderen Klassen dazwischen ..." - vielleicht müssen Sie Transaktionen für diese Anrufer einzeln deklarieren.

Sie können Transaktionen in XML-Konfiguration unter Verwendung von Aspekten deklarieren, entweder mit Spring AOP oder AspectJ. Spring 2.5 und höher bieten Ihnen jetzt die Möglichkeit, Anmerkungen zu verwenden, wenn Sie diese der XML-Konfiguration vorziehen.

Ihre Beschreibung ist fürchterlich verwirrend für mich. Vielleicht ist das ein Teil des Grundes, mit dem du auch Schwierigkeiten damit hast. Ich würde überdenken oder klären.

+0

Ich habe es mit einem traditionellen Inhouse-Framework zu tun, daher ist die Umgestaltung der Spring-Idiome bestenfalls schwierig. Die angegebene Beschreibung ist generisch, aber ich sehe die Verwirrung nicht. Drei verschiedene Instanzen a, b, z von drei verschiedenen Klassen, die auf drei verschiedene Pakete verteilt sind, können von überall in der Anwendung aufgerufen werden und bilden zusammen eine einzelne Transaktion. Die Transaktion wird nur ausgeführt, wenn die dritte Klasse der drei aufgerufen wird. Also, ein wird aufgerufen, b wird aufgerufen und c wird aufgerufen -> commit. B wird aufgerufen, ein ruft, kein c wird aufgerufen, kein Commit. A wird aufgerufen, c wird aufgerufen, commit. –

+0

"Vermächtnis"? Der Frühling ist nicht so alt. Klingt so, als ob die Leute, die damit angefangen haben, Spring nicht kannten. Es würde mir schwer fallen zu glauben, dass das Refactoring mit Spring so schwierig wäre. Ein Objekt muss die Instanzen erstellen, die interagieren. Der Eigentümer der Objekte sollte ebenfalls die Transaktion besitzen. "Überall in der Anwendung" - klingt nach Unordnung. Entschuldigung, kann dir nicht helfen. – duffymo

+0

Ich kann das sagen: Das Framework macht was es soll und ist in dieser Hinsicht gut designed. Ändern sie in Richtung Federn akzeptiert Idiome wäre möglich, aber zu viel Arbeit. –

0

Mit Feder Transaktionen und aop Sie es tun können, aber es wird ein bisschen von „Hack“ ...

Sie müssen den Beginn der Transaktion alle Punkte gesetzt sein werden an den Eingang - können Sie Commit nur von, wenn Sie die Transaktion gestartet haben, und Sie benötigen einen zweiten Aspekt innerhalb dieser, um zu steuern, ob zu begehen oder nicht.

Die einzige Möglichkeit, Spring eine Transaktion rückgängig zu machen, besteht darin, eine Ausnahme über die Transaktionsgrenze zu werfen. Also, was Sie tun müssen, wenn Sie den Bereich Z eingeben, der das Commit verursachen wird, dann müssen Sie etwas in den lokalen Thread (möglicherweise auch über einen Aspekt) einfügen, den dieser "innere" Aspekt finden und somit nicht werfen wird die Ausnahme, um die Transaktion zurückzusetzen.Wenn Sie Z nicht eingeben, erhält der lokale Thread das Flag nicht. Wenn Sie über den inneren Aspekt zurückgehen, wird eine Ausnahme ausgelöst, um die Transaktion rückgängig zu machen. Sie müssen wahrscheinlich diese Ausnahme verschlucken.