24 Stimmen

DAO-Muster - wo passen Transaktionen hinein?

Ich habe also dieses generische DAO-Ding am Laufen und auf den ersten Blick scheint es in Ordnung zu sein. Es ist im Grunde nach der CaveatEmptor Beispielanwendung von den Hibernate Jungs modelliert.

Darüber hinaus habe ich eine Geschäftsschicht, das Herzstück der Anwendung. Sie ist völlig unabhängig von einer bestimmten DAO-Implementierung.

Bis zu diesem Punkt scheint alles in Ordnung zu sein, bis ich anfange, über Transaktionen nachzudenken. Wenn die Implementierung von Transaktionen dem Client überlassen wird, wie in aller Welt kann ich dann die schöne Trennung zwischen meinen Schichten aufrechterhalten? Das heißt, ich verwende derzeit Hibernate, und ich habe nicht wirklich Lust, hibernate-spezifische Transaktionen zu meinem Geschäftsschichtcode hinzuzufügen.

Ich könnte eine einfache Transaktionsschnittstelle mit Begin-, Commit- und Rollback-Methoden erstellen und eine Implementierung an meine Geschäftsschicht übergeben... aber... ich bin mir nicht sicher...

Hier ist also die Herausforderung: Können Sie mir einen Weg empfehlen, dies zu tun, ohne das Wort Spring (oder EJB, oder ein anderes zusätzliches Framework) zu verwenden?

13voto

Nicolas Dorier Punkte 7327

Ich erinnere mich, dass Martin Fowler rät, die Kontrolle über die Transaktion in der Geschäftsschicht zu belassen, da die Transaktion ein Geschäftsproblem ist. (Wenn Sie eine Klasse BankAccount entwerfen, ist eine Transaktion Teil der Domänensprache).

Sie können versuchen, ein TransactionScope zu implementieren, wie es in .NET in etwa so funktioniert

using (TransactionScope ts = new TransactionScope())
{
  ...
}

Es ist das Gleiche wie (nicht genau, aber wenn Sie ein Java-Typ sind, ist es für Sie deutlicher)

TransactionScope scope = new TransactionScope();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}

Um Ihre Geschäftsschicht von DAO-Technologien zu entkoppeln, können Sie in Ihrer Domänensprache eine TransactionFactory hinzufügen, die ein ITransactionScope (eine Schnittstelle) zurückgibt, die Sie mit einer Commit- und Rollback-Methode definiert haben. Auf diese Weise ist Ihre Domänenschicht nicht an Ihre DAO-Schicht gebunden, sondern nur an eine konkrete Implementierung von TransactionFactory.

ITransactionScope scope = transactionFactory.CreateTransaction();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}

7voto

Rogério Punkte 15717

In einer Webanwendung mache ich mir zur Abgrenzung von Transaktionen den HTTP-Anfrage-/Antwort-Zyklus zunutze, wobei jeder atomare Geschäftsvorgang im Rahmen eines dieser Zyklen in einem einzigen dedizierten Thread ausgeführt wird.

Unabhängig davon, welches Web-Framework verwendet wird (Struts, JSF, GWT usw.), gibt es in der Regel eine "Nahtstelle", an der die Abgrenzung von Transaktionen vorgenommen werden kann. In Struts kann dies eine Action-Basisklasse sein. In GWT kann es sich um eine RemoteServiceImpl-Basisklasse handeln.

Verwenden Sie also diesen zentralen Zugriffspunkt, um die Transaktion zu öffnen (bevor Sie die Ausführung des anwendungsspezifischen Codes zulassen) und um sie mit einem Commit zu beenden, wenn keine Ausnahmen aufgetreten sind, oder andernfalls mit einem Rollback (nachdem der anwendungsspezifische Code ausgeführt wurde).

Ich habe diese Strategie ausgiebig bei einer großen und komplexen Webanwendung für Unternehmen angewandt, und sie hat sich als sehr gut erwiesen.

3voto

shrini1000 Punkte 6860

Vielleicht kommt die Antwort etwas zu spät, aber wie wäre es, eine weitere Klasse für bestimmte Transaktionen zu erstellen, die zwischen der Geschäftsschicht und der Dao-Schicht angesiedelt ist? Wenn z.B. die Methoden a() und b() einer DAO in einer Transaktion für eine bestimmte Geschäftsmethode foo() ausgeführt werden sollen, dann erstellen Sie etwas wie fooInTransaction(), das eine Transaktion startet und a() und b() in ihr aufruft. Die Geschäftsmethode foo() wird an sie delegiert.

Dadurch bleibt der Geschäftscode sauber und Doppelarbeit kann durch Re-Factoring vermieden werden.

2voto

JeeBee Punkte 17329

In der Vergangenheit habe ich die Transaktionslogik in der Root-DAO für eine Hierarchie von DAOs untergebracht, die einer Hierarchie von Objekten in Ihrem Modell entsprechen, die eine einzelne solide Entität im System darstellen.

D.h., wenn Sie ein X haben, das viele Ys hat, und Sie wollen Xs und ihre Ys gleichzeitig als ein einziges zusammengesetztes Objekt speichern und abrufen, dann sollte Ihre DAO für X auch die DAO für Y aufrufen. Dann können Sie eine Transaktion um alles in Ihren add()- und update()-Methoden in der DAO für X legen - und sogar das Y DAO-Paket privat machen, um es vor Ihrer Hauptgeschäftslogik zu verbergen. D.h. anstelle der Geschäftslogik:

XDAO xDAO = new XDAO(conn);
xDAO.startTransaction();
boolean success = xDAO.add(x);
if (success)
    for (Y y : x.getYs()) {
        success = YDAO.add(y);
        if (!success) break;
    }
if (success)
    xDAO.commit();
else
    xDAO.rollback();

Sie würden es einfach haben:

XDAO xDAO = new XDAO(conn);
xDAO.add(x);

(mit der Erfolgs-/Commit-/Rollback-Logik innerhalb dieser DAO)

Dies deckt jedoch nicht jede Situation ab, und Ihre Situation kann anders sein (z. B. arbeitet meine mit JDBC, ich weiß nicht, wie Hibernate funktioniert oder ob es dort möglich ist).

1voto

JoshBerke Punkte 64214

Sie haben Recht, dass die Anwendung ein guter Ort ist, um Transaktionen zu koordinieren, da dies die Zusammenstellung komplexerer Aktionen ermöglicht, die von verschiedenen Diensten / Managern / oder wie immer Sie sie nennen wollen, durchgeführt werden.

Eine einfache Lösung besteht darin, eine ITransaction-Schnittstelle zu definieren und eine Art Fabrik oder DI zu verwenden, um den eigentlichen ITransaction-Implementierer vor Ihrer Anwendung zu verbergen. Ich habe meine eigenen so in .net mit nHibernate und im Wesentlichen habe ich eine Basisklasse, dass alle meine Manager (ein Manager in diesem Fall enthält Geschäftslogik für eine logische Menge von Entitäten wie Mitgliedschaft, um die ein oder mehrere Repositories verwenden könnte). Meine Basisklasse hat eine ITransaction BeginTransaction(), die dynamisch einen Typ auf der Grundlage einer Konfigurationsdatei erstellt.

Diese Klasse arbeitet dann mit der Session von nHibernate zusammen, um Transaktionen zu beginnen und zu übertragen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X