Technische Referenz - DataBaskets

Aufbau von DataBaskets

DataBaskets sind Container für Informationen, welche Transaktionen beschreiben. Dazu gehören Entfernen, Hinzufügen oder Editieren von CatalogItems oder StockItems. Die Informationen selbst werden durch Objekte vom Typ DataBasketEntry repräsentiert. DataBasketEntries werden allerdings nicht direkt im DataBasket gespeichert. Stattdessen enthält der DataBasket einen oder mehrere SubDataBaskets, welche ihrerseits die DataBasketEntries beherbergen.

Dadurch, dass mehrere SubDataBaskets gespeichert werden können, erhält man die Möglichkeit, mit einem DataBasket mehrere "Speicherpunkte" zu unterhalten. Um das dadurch eingeführte Problem der möglichen Datenkorruption durch Inkonsistenzen muss sich der Programmierer kümmern, das Framework führt keine Konsistenzprüfungen durch.

In der Praxis wird meist ein DataBasket mit genau einem SubDataBasket verwendet. Das ist auch die Standardkonfiguration beim Erzeugen eines neuen DataBaskets.


Aufbau von DataBasketEntries

DataBasketEntries beschreiben Transaktionen von Stock- oder CatalogItems und besitzen dafür die folgenden Attribute:

mainKeyUnterscheidung des Typs von DataBasketEntry: für StockItems oder für CatalogItems.
secondaryKeyDer Key des Catalog- oder StockItems, für welches der DataBasketEntry eine Transaktion beschreibt.
valueDas Catalog- oder StockItem, für welches der DataBasketEntry eine Transaktion beschreibt. Der Key dieses Items entspricht genau dem secondaryKey des DataBasketEntrys.
sourceDer Container (Catalog oder Stock), aus dem das Item entfernt wurde.
destinationDer Container (Catalog oder Stock), zu dem das Item hinzugefügt wurde.
ownerDer DataBasket, zu dem dieser DataBasketEntry gehört.
handledBeschreibt, ob schon ein commit oder rollback auf diesem DataBasketEntry durchgeführt wurde.

DataBasketConditions

DataBasketConditions sind Filter für DataBasketEntries. Deshalb besitzen sie auch die meisten der Attribute von DataBasketEntries:

mainKeyUnterscheidung des Typs von DataBasketEntry: für StockItems oder für CatalogItems.
secondaryKeyDer Key des Catalog- oder StockItems, für welches der DataBasketEntry eine Transaktion beschreibt.
valueDas Catalog- oder StockItem, für welches der DataBasketEntry eine Transaktion beschreibt.
sourceDer Container, aus dem das Item entfernt wurde.
destinationDer Container, zu dem das Item hinzugefügt wurde.

Außerdem gibt es eine match() Methode, die standardmäßig true zurückliefert, in speziellen Fällen aber überschrieben werden muss (siehe checkItems(boolean fGet)).

Falls ein Attribut den Wert null besitzt, so wird dies vom Framework dahingehend interpretiert, dass der Filter bei diesem Attribut immer passt. Es gibt sozusagen kein Filterkriterium, welches angewandt werden müsste.

DataBasketConditions kommen unter Anderem beim Iterieren über SubDataBaskets zur Anwendung (siehe Iterieren über SubDataBaskets).


SubDataBaskets

Aufbau von SubDataBaskets

Das Herzstück eines SubDataBaskets ist eine HashMap dbeCatagories. In ihr befinden sich wiederum zwei HashMaps, eine für StockItemDBEntries und eine für CatalogItemDataBasketEntries. Jede dieser zwei Maps enthält Listen. In diesen Listen stehen die eigentlichen DataBasketEntry-Objekte. Der Key, unter dem die Listen in ihrer Map gespeichert sind, ist gleich dem Key des Items, welches in Form eines DataBasketEntrys gespeichert werden soll (also dem secondaryKey des DataBasketEntrys). Damit ist es möglich, Items gleichen Typs und Namens, z.B. zwei CatalogItems, die beide den Namen "item1" haben, im selben SubDataBasket zu speichern, sprich, Kollisionen zu erlauben. Abbildung 2.1 verdeutlicht den Aufbau noch einmal grafisch.



Aufbau eines SubDataBaskets
Abbildung 2.1: Aufbau eines SubDataBaskets

An dieser Stelle bietet sich ein Beispiel an: Wenn drei mal je fünf StockItems "item1" aus einem CountingStock entfernt werden, gibt es drei DataBasketEntries, die in einer gemeinsamen Liste in der Map für die StockItemDBEntries stehen. Auf diese Liste kann vom Framework über den Key "item1" zugegriffen werden. Die drei DataBasketEntries sehen hier identisch aus, und zwar folgendermaßen:

mainKey__MAINKEY:_STOCKITEM_IMPL
secondaryKeyitem1
value5 (bei Items für StoringStocks stände hier das tatsächliche StockItem Objekt)
sourcedas Stock Objekt, aus dem die Items entfernt wurden
destinationnull

Iterieren über SubDataBaskets

SubDataBaskets haben ihren eigenen Iterator, auf den mit der Methode iterator(DataBasketCondition dbc, boolean fAllowRemove, boolean fShowHandled) zugegriffen werden kann.

Der verschachtelte Aufbau von SubDataBaskets macht es nötig, in mehreren Stufen zu iterieren:

Alle drei Iteratoren sind global in der Klasse SubDataBasket definiert.

hasNext() und next()

Die Methoden hasNext() und next() machen beide Gebrauch von findNext(). Während hasNext() findNext() mit dem Parameter fGet = false aufruft und den erhaltenen booleschen Rückgabewert selbst zurückliefert, ist fGet für next() true. Der Rückgabewert ist die globale Variable dbeCurrent, in welcher der nächste zurückzuliefernde DataBasketEntry abgelegt wurde. Das Speichern des Entrys in dbeCurrent geschieht in der Methode checkItems(boolean fGet).

findNext(boolean fGet)

Die Methode besteht aus einer do-while-Schleife. Zu Beginn wird checkSubCategories(boolean fGet) aufgerufen. Wird dabei true zurückgeliefert, so wurde in der aktuellen Category (entweder die Map für StockItems oder die für CatalogItems) eine SubCategory mit passendem DataBasketEntry gefunden und, falls fGet true ist, wurde dieser Entry in dbeCurrent gespeichert. Es wird true zurückgeliefert und die Methode ist beendet.

Konnte kein DataBasketEntry gefunden werden, muss die nächste Category, falls es noch eine gibt, durchsucht werden. Diese erhält man, indem man den Iterator iCategories weiterlaufen lässt. Auf der neuen Category wird ein Iterator erzeugt und als iSubCategories gespeichert, er wird dann beim nächsten Durchlauf der do-while-Schleife von checkSubCategories() verwendet, um über die SubCategories dieser Category zu iterieren. Falls dem Iterator des DataBaskets im Konstruktor eine DataBasketCondition übergeben wurde, wird vor dem Weiterspringen zur nächsten Category überprüft, ob der mainKey der Condition null ist, bzw. ob die mainKeys von DataBasketEntry und DataBasketCondition identisch sind. Falls nicht, würde diese Category ausgelassen und im folgenden Durchlauf der do-while-Schleife die nächste untersucht. Gibt es keine Category mehr, wird findNext() mit return false verlassen.

checkSubCategories(boolean fGet)

Die Methode arbeitet absolut analog zu findNext(), nur eine Hierarchieebene tiefer. So wird zu Beginn der do-while-Schleife mit checkItems(boolean fGet) überprüft, ob passende DataBasketEntries in den Items der SubCategory zu finden sind. Falls ja, wird die Methode mit return true beendet, andernfalls wird zur nächsten SubCategory gegangen, auf der der Iterator mItems definiert wird, um über die Items der SubCategory zu laufen. Auch hier muss wieder die DataBasketCondition beachtet werden. Eine SubCategory kommt in Frage, wenn der secondaryKey der Condition mit dem Key der SubCategory übereinstimmt oder null ist.

checkItems(boolean fGet)

Die Methode überprüft zuerst dbeNext. Wenn es nicht null ist, wurde in einem früheren Iterationsschritt (ausgelöst von hasNext()) schon das Vorhandensein eines passenden DataBasketEntrys festgestellt und es wird true zurückgeliefert. Wenn zusätzlich fGet true ist, also checkItems() von der next() Methode aufgerufen wurde, wird vorher dbeNext in dbeCurrent gespeichert. Wenn dbeNext aber null ist, ist nicht bekannt, ob es einen weiteren DataBasketEntry gibt. Deshalb wird unter Benutzung von iItems über die aktuelle Liste von Items iteriert. Für jeden DataBasketEntry wird überprüft, ob er den Suchkritieren entspricht. Das ist der Fall wenn:

Wird ein passender DataBasketEntry gefunden, so wird er in dbeNext gespeichert, oder, wenn fGet true ist, gleich in dbeCurrent. Anschließend wird die Methode mit return true beendet. Wurde in der Liste kein passender Eintrag gefunden, wird false zurückgeliefert.


Commit von DataBasketEntries

Ein commit() wird vom DataBasket an all seine SubDataBaskets weitergeleitet. Es kann eine DataBasketCondition übergeben werden. Das commit() wird dann nur für DataBasketEntries, die auf die DataBasketCondition passen, ausgeführt.
Alternativ kann man mit commitSubBasket() auch einen bestimmten SubDataBasket angeben, für den ein commit durchgeführt werden soll. Es ist allerdings nicht möglich, einzelne SubDataBaskets für ein commit auszuwählen und ihnen gleichzeitig eine DataBasketCondition als Filter mitzugeben. Diese Funktionalität kann aber bei Bedarf leicht in einer Unterklasse von DataBasketImpl realisiert werden.

SubDataBaskets leiten das commit zu ihren Entries. Es wird mit der übergebenen DataBasketCondition über den SubDataBasket iteriert (siehe Iterieren über SubDataBaskets). Auf jeden DataBasketEntry, der vom Iterator geliefert wird, wird ein commit() ausgeführt, sofern der DataBasketEntry noch nicht als handled (bearbeitet) markiert ist. Anschließend wird er mit der remove() Methode des Iterators aus dem SubDataBasket entfernt.

Beim commit eines DataBasketEntries wird handled auf true gesetzt, um anzuzeigen, dass dieser Entry bearbeitet wurde und keine aktuelle Transaktion mehr beschreibt. Danach wird für die Quelle des DataBasketEntries, die im Feld source steht, ein commitRemove() ausgeführt, für das Ziel des Entrys, welches vom Attribut destination beschrieben wird, ein commitAdd(). Damit werden temporär hinzugefügte oder entfernte Items in ihren Quellen fest hinzugefügt bzw. komplett entfernt. Für Details zu diesen Methoden sei auf die Dokumentation von Katalogen und Stocks verwiesen.


Rollback von DataBasketEntries

Ein rollback() funktioniert analog zum commit(). Beim rollback() auf dem DataBasketEntry wird ein rollbackAdd() bzw. ein rollbackRemove() ausgeführt, was zur Folge hat, dass temporär zu einer Quelle hinzugefügte Items wieder entfernt werden und temporär entfernte Items wieder hinzugefügt.
Auch zu diesen Methoden stehen Details in der Dokumentation von Catalogs und Stocks.


Die Bedeutung des Flags "handled"

Das Flag handled scheint auf den ersten Blick überflüssig zu sein, schließlich wird der abgearbeitete DataBasketEntry sofort aus dem SubDataBasket entfernt, wenn auf diesem ein commit() oder rollback() ausgeführt wird. Allerdings ist es möglich, für einen einzelnen DataBasketEntry "von Hand" ein commit() oder rollback() auszuführen und ihn einfach nicht aus dem SubDataBasket zu löschen. Um in solchen Fällen Datenintegrität zu sichern, werden bearbeitete DataBasketEntries immer mit handled entwertet.


previous EinleitungCatalogs next



by s6200595