1 Themenübersicht
Das Änderungsjournal ist eine Protokollfunktion für die Historie von betriebswirtschaftlichen Größen und zeichnet Ereignisse im Lebenszyklus von Business Entitys auf. Pro Business Entity und Datenbank-Instanz kann festgelegt werden, ob Änderungen protokolliert werden sollen. Der Persistenzdienst sorgt also nicht nur für die Änderungsinformationen an Business Entitys, sondern gleichfalls für die Einträge im Änderungsjournal. Nur erfolgreich abgeschlossene Toplevel-Transaktionen werden protokolliert.
Mit dem Änderungsjournal können beispielsweise Änderungen an Stammdaten lückenlos dokumentiert werden und sind daher nachvollziehbar. Das Änderungsjournal kann mithilfe von Anpassungsprogrammierung für beliebige Business Entitys aktiviert und abgefragt werden.
Die Übertragungsliste zeichnet im Unterschied zum Änderungsjournal nur auf, dass eine Business-Entity-Instanz seit der letzten Verarbeitung dieser Instanz verändert wurde. Die Übertragungsliste kann insbesondere beim Datenaustausch mit Fremdsystemen verwendet werden.
2 Zielgruppe
- Entwickler
- Technische Berater
3 Beschreibung
Das Änderungsjournal wird über eine Programmierschnittstelle eingebunden. Damit das Änderungsjournal genutzt werden kann, muss die Aufzeichnung der Änderungen pro Business Entity durch ein Programm aktiviert werden.
Bei jeder Änderung eines aufgezeichneten Business Entitys oder eines von dessen Dependents werden Einträge in das Änderungsjournal geschrieben. Es gibt folgende Arten von Einträgen:
- Neu
Bei der Neuanlage eines Business Objects werden alle Attribute des neuen Objekts gespeichert.
- Ändern
Bei der Änderung eines Business Objects werden die geänderten Attribute mit ihrem alten und neuen Wert gespeichert.
- Löschen
Beim Löschen eines Business Objects werden alle Attribute des gelöschten Objekts gespeichert.
Zu jedem Eintrag wird gespeichert, durch welchen Benutzer und zu welchem Zeitpunkt dieser erzeugt wurde. Damit ist die vollständige Änderungshistorie eines Business Entitys nachvollziehbar.
Beispiel für die Aufzeichnung des Änderungsjournals
In dem Beispiel wird zunächst ein Auftrag mit einer Position durch Benutzer X erfasst, dann eine weitere Position durch Benutzer Y hinzugefügt und danach wird der vollständige Auftrag durch Benutzer X gelöscht.
Das Änderungsjournal zeichnet alle Objektzugriffe (putObject, deleteObject) über den „CisObjectManager“ auf. Änderungen durch „putUpdateStatement“ werden nicht aufgezeichnet. „UpdateStatements“ werden für betriebswirtschaftlich relevante Daten meist nur bei Reorganisationen eingesetzt. Wenn die Tabellen direkt mit Datenbankmitteln (SQL) geändert werden, dann werden die Änderungen nicht aufgezeichnet.
Beim Ändern und Löschen wird jeweils auch der alte Zustand des geänderten Business Objects im Änderungsjournal gespeichert. Dadurch können nicht aufgezeichnete Änderungen gefunden werden.
Im Änderungsjournal werden nur die Inhalte des überwachten Business Entitys aufgezeichnet. Ein Business Entity referenziert im Allgemeinen andere Business Entitys über deren technische Schlüssel (häufig GUIDs). Wenn diese referenzierten Business Entitys nicht überwacht werden, dann kann es sein, dass diese irgendwann gelöscht werden. In diesem Fall kann der technische Schlüssel nicht mehr aufgelöst werden.
Beispiel:
Wenn der Vertriebsauftrag überwacht wird, nicht aber der Partner, dann kann beispielsweise die Beziehung zum Auftraggeber nicht mehr aufgelöst werden, wenn der entsprechende Partner reorganisiert wurde.
Wenn Sie ein Business Entity überwachen, dann sollten Sie auch die wichtigsten von diesem Business Entity referenzierten Business Entitys ebenfalls überwachen. Das betrifft insbesondere die Stammdaten, wie beispielsweise „Partner“ und „Artikel“. Wenn ein referenziertes Business Entity gelöscht wurde, dann kann das referenzierte Business Entity auch über das Änderungsjournal geöffnet werden.
Wenn Sie ein Business Entity mit dem Änderungsjournal überwachen, dann beachten Sie bitte,
- dass das Änderungsjournal unter Umständen sehr groß werden kann und
- dass das Schreiben des Änderungsjournals eine höhere Datenbanklast erzeugt.
Das trifft insbesondere zu, wenn das Änderungsjournal für Business Objects mit sehr vielen Objekten aktiviert wird oder wenn die Business Objects eine sehr hohe Änderungsfrequenz haben.
Stellen Sie vor der Aktivierung des Änderungsjournals sicher, dass Ihr System die zusätzlichen Daten mit akzeptablem Zeit- und Speicherverbrauch verarbeiten kann.
4 Programmierschnittstelle
Über den „ModificationJournalManager“ kann das Änderungsjournal gesteuert werden. Der „ModificationJournalManager“ kann von dem „CisEnvironment“ abgefragt werden. Der „ModificationJournalManager“ umfasst Methoden, um das Änderungsjournal für ein Business Entity zu aktivieren bzw. zu deaktivieren und um das Änderungsjournal abzufragen.
Die Verwendung der Programmierschnittstelle des „ModificationJournals“ wird in den folgenden Kapitel beschrieben.
4.1 Aktivieren des Änderungsjournals
Das Änderungsjournal kann mithilfe der Anwendung „Änderungsjournal aktivieren“ für Business Entitys aktiviert werden. Zusätzlich dazu kann das Änderungsjournal auch über den „CisModificationJournalManager“ aktiviert und deaktiviert werden.
Das Änderungsjournal wird mit der Methode setActive für ein Business Entity auf einer Datenbank aktiviert bzw. deaktiviert.
void setActive(byte[] databaseGuid, byte[] classGuid,
boolean active);
Mit der Methode isActive kann abgefragt werden, ob das Änderungsjournal für ein Business Entity auf einer Datenbank aktiv ist:
boolean isActive(byte[] databaseGuid, byte[] classGuid);
Beispiel
Aktivierung des Änderungsjournals für das Business Entity „Partner“ auf der aktiven OLTP-Datenbank:
CisEnvironment env = CisEnvironment.getInstance();
CisModificationJournalManager mjm =
env.getModificationJournalManager();
mjm.setActive(env.getDatabaseGuid(),Partner._classGuid,true);
4.2 Dynamische Business Objects
Dynamische Business Objects erweitern entweder ein bestehendes Business Entity oder stehen für sich und haben damit keinen Bezug zu einem Business Entity. Damit Änderungen an dynamischen Business Objects sinnvoll aufgezeichnet werden können, muss eine Beziehung zwischen der Instanz des dynamischen Business Objects und einer Business-Entity-Instanz bestehen.
Dynamische Business Objects, die als Objekterweiterungen erfasst wurden, wie beispielsweise die benutzerdefinierten Felder und die Artikelmerkmale, haben eine Beziehung zu genau einem Business Entity. Dynamische Business Objects, die aus einem Business Entity über eine GUID referenziert werden, haben diese Beziehung nicht, z. B. „Parameter“ in der Produktion.
Wenn ein dynamisches Business Object keine Beziehung zu einem Business Entity hat, dann kann mithilfe der Methode
void set_entityKey(byte[] entityTimeDependentKey)
die Beziehung hergestellt werden. Wenn das Business Entity zeitabhängig ist, muss der zeitabhängige, ansonsten der normale Primärschlüssel am dynamischen Business Object gesetzt werden. Genauso kann bei einem dynamischen Business Object auch der „InstanceString“ gesetzt werden:
void set_instanceString(String instanceString)
Der Business-Entity-Key und der „InstanceString“ müssen an der Instanz des dynamischen Business Objects gesetzt werden, bevor die Instanz mit „putObject“ an den „Transaction-Manager“ übergeben wird.
4.3 Abfragen des Änderungsjournals
Über den „ModificationJournalManager“ kann das Änderungsjournal abgefragt werden. Die Änderungen im Änderungsjournal können entweder pro Business-Entity-Instanz zusammengefasst oder jede Änderung kann einzeln ausgegeben werden.
Änderungen einer Business-Entity-Instanz werden in Objekten der Klasse „JournalEntity“ zurückgegeben.
4.3.1 Abfrageobjekt: JournalQuery
Ein Objekt der Klasse „JournalQuery“ kann vom „ModificationJournalManager“ durch die Methode „createJournalQuery“ erzeugt werden. Die „JournalQuery“-Klasse enthält die Abfrageparameter für eine Abfrage des Änderungsjournals. Verwenden Sie die Konstantennamen, um die Abfrage des Änderungsjournals nach dem zugehörigen Abfrageparameter einzuschränken. Verwenden Sie die Klasse „com.cisag.pgm.objsearch.SelectionSupport“, um den „Selection-String“ für einen Abfrageparameter zu setzen.
4.3.2 Änderungen einzeln abfragen
Wenn jede Änderung einzeln abgefragt wird, dann wird für jede Änderung ein „JournalEntity“ erzeugt und einzeln zurückgegeben. Die Änderungen werden also nicht weiter zusammengefasst. Sie müssen eine Transaktion auf der Datenbank geöffnet haben, auf der Sie das Änderungsjournal abfragen möchten. Danach können Sie mit der Methode
CisIterator retrieveJournalEntities(JournalQuery query);
das Änderungsjournal für eine „JournalQuery“ abfragen. Während Sie das Änderungsjournal abfragen, muss die Transaktion offen bleiben. Nach der Abfrage des Änderungsjournals muss erst der Iterator und danach, falls notwendig, die Transaktion geschlossen werden.
4.3.3 Änderungen zusammengefasst abfragen
Wenn die Änderungen zusammengefasst abgefragt werden, dann werden alle Änderungen einer Business-Entity-Instanz in einem „JournalEntity“ gespeichert. Dadurch lässt sich die Änderungshistorie eines Objekts gut auswerten. Der Nachteil ist, dass die vollständige Business-Entity-Instanz mit allen Änderungen in den Hauptspeicher geladen wird. Bei Instanzen mit sehr vielen Dependents oder sehr vielen Änderungen kann das problematisch sein.
Eine Transaktion muss auf der Datenbank geöffnet sein, auf der Sie das Änderungsjournal abfragen möchten. Danach können Sie mit der Methode
Collection retrieveJournalEntities(JournalQuery query);
das Änderungsjournal für eine „JournalQuery“ abfragen. Nach der Abfrage des Änderungsjournals können Sie die Transaktion schließen.
Aus historischen Gründen stehen noch die folgenden Abfragemethoden bereit. Diese sollten jedoch zukünftig nicht mehr verwendet werden. Mithilfe der Methoden
Collection retrieveJournalEntities(byte[] databaseGuid, byte[] key,
Date from, Date until);
Collection retrieveJournalEntities(byte[] databaseGuid, byte[] key,
Date validFrom,
Date from, Date until);
kann das Änderungsjournal für ein bestimmtes Business Entity mit dem Primärschlüssel des Business Entitys (key) für einen bestimmten Zeitraum (from<=t<until) abgefragt werden.
Alternativ dazu kann das Änderungsjournal auch über den „InstanceString“
Collection retrieveJournalEntitysByInstanceString(
byte[] databaseGuid, byte[] entityClassGuid,
String instanceSelectionString,
String changeTimeSelectionString,
String entityUpdateTypeSelectionString);
abgefragt werden. Verwenden Sie die Klasse com.cisag.pgm.objsearch.SelectionSupport um die „Selection-Strings“ zu berechnen.
4.3.4 Zugriff auf die Änderungen
Für jede geänderte Business-Entity-Instanz besteht ein Objekt der Klasse „JournalEntity“ von dem alle Änderungen dieser Instanz abgefragt werden können. Die Methoden zum Abfragen des Änderungsjournals geben Objekte dieser Klasse zurück. Mithilfe der Methode
JournalChange[] getChanges();
kann der Änderungszeitpunkt und der ändernde Benutzer für jede Änderung der Business-Entity-Instanz abgefragt werden. Jeder Eintrag in dem zurückgegebenen Feld identifiziert eine Änderung. Der Index in diesem Feld wird im Folgenden verwendet, um auf die entsprechende Änderung zuzugreifen.
Die Klasse „JournalObject“ enthält die geänderten Werte eines Business Objects. Die Änderungen direkt am Business-Entity-Objekt können mit der Methode
JournalObject getEntity();
abgefragt werden. Die Änderungen an den Dependents werden mit der Methode
JournalObject[] getObjects(byte[] classGuid);
abgefragt. Die „ClassGuids“ der Dependents und des Business Entitys können mit der Methode
CisSet getClassGuids();
ermittelt werden.
Die Änderungen einer Business-Object-Instanz werden in Objekten der Klasse „JournalObject“ geführt. Die Attributpfade des Business Objects können mit der Methode
Set getAttributePaths();
abgefragt werden. Parts werden dabei nicht als komplexe Objekte zurückgegeben. Die Part-Attribute werden ausgelöst, d. h. es werden wie in OQL die Attributpfade zu den primitiven Spalten ermittelt. Eine Ausnahme bilden die in der Klasse „ColumnTyp“ aufgeführten „Specialparts“. Der Wert eines Attributs wird über den Attributpfad mit der Methode
ChangedValue getChangedValue(String attributePath,
int changeIndex);
abgefragt. Der Parameter „changeIndex“ entspricht dem Index aus der Methode „JournalEntity:getChanges()“. Für Attribute, die sich bei dem Änderungsindex nicht geändert haben, wird „null“ zurückgegeben.
Die Art der Änderung des gesamten Business Objects wird von der Methode
short[] getUpdateTypes();
zurückgegeben. Die Konstanten stehen in der Klasse „UpdateType“ und haben die folgende Bedeutung:
Konstante | Bedeutung |
DELETE | Das Business Object wurde bei der Änderung gelöscht.
Die Inhalte des gelöschten Business Objects stehen in den alten Attributwerten (Time.OLD). |
INSERT | Das Business Object wurde bei der Änderung neu erfasst.
Die Inhalte des neuen Business Objects stehen in den neuen Attributwerten (Time.NEW). |
UPDATE | Das Business Object wurde bei der Änderung geändert.
Die geänderten Attribute stehen mit dem Wert vor (Time.OLD) und nach (Time.OLD) der Änderung zur Verfügung. |
NOT_MODIFIED | Das Objekt wurde nicht verändert.
Keine Attributwerte liegen vor. |
Die alten und neuen Werte eines Attributs sind in Objekten der Klasse „ChangedValue“ gespeichert. Diese Klasse hat für jeden Datentyp (UpdateType) spezielle Abfragefunktionen, z. B.:
byte getByte(short time);
Beim Abfragen eines Wertes muss angegeben werden, ob der alte oder der neue Wert benötigt wird (Time). Der Datentyp (ColumnType) des alten bzw. des neuen Wertes kann mit der Methode
short getType(short time);
bestimmt werden.
4.4 Beispiel für das Abfragen des Änderungsjournals
Das folgende stark vereinfachte Beispielprogramm zeigt, wie das Änderungsjournal abgefragt werden kann:
void processOptions() {
…
CisModificationJournalManager mjm =
env.getModificationJournalManager();
// create journal query
JournalQuery query = mjm.createJournalQuery();
query.setSelection(JournalQuery.ENTITY_CLASS_GUID,
SelectionSupport.
toGuidSelection(Parner._class)) ;
// retrieve changed entity
Collection entities =
jm.retrieveJournalEntities(query);
// iterate over each changed entity
for (Iterator i=entities.iterator(); i.hasNext();) {
JournalEntity entity = (JournalEntity)i.next();
// process changes of each entity
JournalChange[] changes = entity.getChanges();
for (int l=0; l<changes.length; l++) {
// process all changed business objects
for (CisBytesIterator j=entity.getClassGuids().iterator();
j.hasNext();) {
byte[] classGuid = j.next();
JournalObject objects[] = entity.getObjects(classGuid);
for (int k=0; k<objects.length; k++) {
processJournalObject(objects[k],l);
}
}
}
}
…
}
void processJournalObject(JournalObject object, int changeIndex) {
…
switch (object.getUpdateTypes()[changeIndex]) {
case UpdateType.DELETE:
…
}
// iterate all attributes
for (Iterator i= object.getAttributePaths().iterator();
i.hasNext();) {
String attributePath = (String)i.next();
// check whether the attribute was changed
ChangedValue value =
object.getChangedValue(attributePath,changeIndex);
if (value==null) {
continue;
}
// display changed values
switch (object.getUpdateTypes()[changeIndex]) {
case UpdateType.DELETE:
processValue(
getValueString(value,
CisModificationJournalManager.Time.OLD));
break;
case UpdateType.INSERT:
processValue(
getValueString(value,
CisModificationJournalManager.Time.NEW));
break;
case UpdateType.UPDATE:
processValue(
getValueString(value,
CisModificationJournalManager.Time.OLD));
processValue(
getValueString(value,
CisModificationJournalManager.Time.NEW));
break;
}
}
}
String getValueString(ChangedValue value, short time) {
switch (value.getType(time)) {
case ColumnType.BINARY:
return ByteArrayUtility.toHex(value.getBytes(time));
case ColumnType.BLOB:
return “BLOB”;
case CisModificationJournalManager.ColumnType.BOOLEAN:
return Boolean.toString(value.getBoolean(time));
…
}
}
4.5 Übertragungsliste
Die Übertragungsliste zeichnet auf, ob eine Business-Entity- oder Dependent-Instanz erfasst, gelöscht oder verändert wurde. Die Übertragungsliste zeichnet im Unterschied zum Änderungsjournal nicht auf, welche Inhalte eine Business-Object-Instanz hat bzw. welche dieser Inhalte wie verändert wurden. Bei der Aufzeichnung der Übertragungsliste bestimmt der „CisTransferListController“ welche Änderungen in der Übertragungsliste aufgezeichnet werden.
Beispiel:
Ein Vertriebsauftrag soll an ein Fremdsystem übertragen werden, wenn der Vertriebsauftrag komplett geliefert und fakturiert ist. Der „CisTransferListController“ für den Vertriebsauftrag bestimmt, dass Änderungen nur dann aufgezeichnet werden, wenn die Attribute „deliveryState“ und „invoicingState“ den Wert „COMPLETE“ haben.
Die Übertragungsliste kann nur für Business Objects aufgezeichnet werden, deren Primärschlüssel aus einer oder zwei GUIDs besteht.
Beachten Sie, dass die Übertragungsliste erst nach der Aktivierung die Änderungen der Business Entitys überwacht. Weiterhin werden Änderungen mit OQL (Update, Insert oder Delete) nicht von der Übertragungsliste aufgezeichnet. In vielen Fällen ist daher notwendig, dass unabhängig von der Übertragungsliste ein vollständiger Datenabgleich z. B. mit dem Fremdsystem manuell ausgeführt werden kann.
4.5.1 Aufzeichnung der Übertragungsliste
Ein „CisTransferListController“ steuert die Aufzeichnung der Übertragungsliste für ein oder mehrere Business Objects. Für jeden Anwendungsfall der Übertragungsliste muss eine abgeleitete Instanz der Klasse „CisTransferListController“ im Hook „com.cisag.pgm.appserver.hook.TransferListRegistryHook“ der Hook-Contract-Definition „com.cisag.pgm.appserver.Server“ registriert werden.
Jeder „CisTransferListController“ wird durch den Typ eindeutig identifiziert. Alle verwendeten Typen sind im Valueset „com.cisag.app.general.TransferListType“ registriert. Wenn ein neuer „CisTransferListController“ entwickelt wird, dann muss ein neuer Eintrag diesem Valueset hinzugefügt werden.
Alternativ kann ein „CisTransferListController“ durch einen Sub-Typ eindeutig identifiziert werden. Der Sub-Typ ist der Name eines beliebigen Logischen Datentyps. Der Logische Datentyp sollte nur für die Registrierung des Controllers verwendet werden. Der primitive Typ des Logischen Datentyps wird nicht ausgewertet. Durch die Verwendung eines Sub-Typs können Sie einen Transfer-List-Controller konfliktfrei erfassen. Sub-Typen erlauben insbesondere das Erfassen von Transfer-List-Controllern in Apps.
Im Konstruktor des „CisTransferListControllers“ wird der Typ bzw. Sub-Typ festgelegt und welche Business Objects dieser Controller überwacht. Für jedes überwachte Business Object muss der Methode „addEntity“ bzw. „addDependent“ im Konstruktor eine passende Instanz der Inner-Class „CisTransferListController.Entity“ bzw. „CisTransferListController.Dependent“ übergeben werden.
Die Methode „isToBeRecorded“ des Business Entitys beziehungsweise des Dependents bestimmt, ob eine Änderung an dem überwachten Business Entity in die Übertragungsliste geschrieben wird. Wenn diese Methode „true“ zurückliefert, dann wird die Änderung in der Übertragungsliste aufgezeichnet. Bei „false“ wird die Änderung nicht aufgezeichnet. Den Methoden wird der persistente Zustand (Parameter old…) und der in der Transaktion geänderte Zustand (Parameter new…) des Business Entitys und beziehungsweise auch des Dependents übergeben. Damit können Sie nachvollziehen, welche Daten in der Transaktion geändert wurden.
Wenn eine Business-Object-Instanz gelöscht wurde, dann können Sie diese beim Auswerten der Übertragungsliste nicht mehr öffnen. Aus diesem Grund kann an der Übertragungsliste eine kurze Zeichenkette als Referenz hinterlegt werden, um auch gelöschte Instanzen z. B. für externe Systeme identifizieren zu können.
Die Methode „getReference“ des Business Entitys beziehungsweise des Dependents kann optional eine Zeichenkette zur Identifikation des geänderten Objekts zurückgeben. Die Identifikation sollte für ein Objekt konstant sein und sollte sich nicht abhängig vom Zustand des Objekts ändern. Der Business-Key ist in den meisten Fällen eine geeignete Identifikation. Die Methode wird nach der Methode „isToBeRecorded“ immer dann aufgerufen, wenn die Änderung aufgezeichnet werden soll. Sie müssen also die Prüfungen aus der Methode „isToBeRecorded“ in „getReference“ nicht erneut implementieren. Innerhalb der Methoden „isToBeRecorded“ und „getReference“ dürfen die übergebenen Business-Object-Instanzen auf keinen Fall verändert werden noch sollten dort komplexe Berechnungen oder Datenbankzugriffe erfolgen. Da diese Methode für jede Änderung an dem überwachten Business Object aufgerufen wird, kann die Laufzeit dieser Methode die Leistung des Gesamtsystems beeinträchtigen. Komplexe Prüfungen sollten bei der Auswertung der Übertragungsliste erfolgen.
Eine Business-Object-Instanz wird über den Primärschlüssel identifiziert. Zeitabhängigkeit wird in der Übertragungsliste nicht beachtet. Das bedeutet, dass beim Abfragen der Übertragungsliste nicht mehr nachvollzogen werden kann, welche Version einer Business-Entity-Instanz verändert wurde.
Die Methode „isActive“ gibt an, ob die Aufzeichnung der Übertragungsliste für „CisTransferListController“ auf einer Datenbank aktiv ist. Das Ergebnis dieser Methode muss aus dem Customzing der Datenbank berechnet werden. Diese Methode wird nur nach dem Application-Server-Start oder bei Änderungen des Customizings aufgerufen.
Beispiel für einen CisTransferListController
Der „OrderTransferListController“ zeichnet das Erfassen oder die Löschung einer Instanz des Business Entitys „Order“ sowie die Änderungen des Attributs „state“ auf.
public class OrderTransferListController extends
CisTransferListController {
public class OrderEntity extends Entity<Order> {
OrderDataEntity() {
super(Order.class);
}
public boolean isToBeRecorded(
Order oldEntity, Order newEntity) {
if (oldEntity!=null && newEntity!=null) {
return oldEntity.getState()!=
newEntity.getState();
} else {
return true;
}
}
public String getReference(
Order oldEntity, Order newEntity) {
if (oldEntity!=null) {
return oldEntity.getNumber();
} else {
return newEntity.getNumber();
}
}
}
public class OrderDetailDependent
extends Dependent<OrderDetail,Order> {
OrderDetailDependent() {
super(OrderDetail.class);
}
public boolean isToBeRecorded(
OrderDetail oldDependent, OrderDetail newDependent) {
Order oldEntity, Order newEntity) {
if (oldDependent!=null && newDependent!=null) {
return oldDependent.getState()!=
newDependent.getState();
} else {
return true;
}
}
}
public OrderTransferListController() {
super(TransferListType.ORDER_CONTROLLER);
addEntity(new OrderEntity());
addDependent(new OrderDetailDependent());
}
public boolean isActive(byte[] databaseGuid) {
boolean result;
…
sm.getConfiguredValues(
databaseGuid,
ORDER_FUNCTION_NAME);
…
return result;
}
}
Wenn Sie statt des Typs einen Sub-Typ verwenden, müssen Sie beispielsweise den Logischen Datentyp „com.my.app.general.OrderTransferListController“ erfassen und den Konstruktor vom Controller wie folgt ändern:
public OrderTransferListController() {
super(“com.my.app.general.OrderTransferListController”);
addEntity(new OrderEntity());
}
Der Controller kann wie folgt in einer Implementation der Hook-Contract-Definition com.cisag.pgm.appserver.Server registriert werden:
Class MyTranferListRegistry implements TransferListRegistryHook {
public void initialize(TransferListRegistry registry) {
registry.add(new OderTransferListController());
}
}
4.5.2 Abfragen der Übertragungsliste
Die Übertragungsliste kann über die Methode „retrieveTransferListEntries“ des „CisModificationJournalManagers“ abgefragt werden. Damit ein konsistenter und konstanter Stand der Übertragungsliste abgefragt werden kann, ist notwendig, mit der Methode „newTransferListSequence“ eine neue Sequenznummer zu erzeugen. Alle neuen Einträge in der Übertragungsliste werden danach mit dieser neuen Sequenznummer aufgezeichnet. Beim Abfragen der Übertragungsliste wird die neue Sequenznummer übergeben. Die Methode „retrieveTransferListEntries“ liefert damit nur Einträge zurück, die mit einer älteren Sequenznummer als der übergebenen geschrieben wurden.
Die Einträge der Übertragungsliste werden durch die Inner-Class „CisModificationJournalManager.TransferListEntry“ dargestellt. Für jede Business-Entity-Instanz wird beim Abfragen maximal ein „TransferListEntry“ zurückgegeben, unabhängig davon, wie häufig diese geändert wurde. Wenn ein „TransferListEntry“ erfolgreich verarbeitet wurde, dann wird die Änderung mit der Methode „delete“ entfernt. Der „TransferListEntry“ wird damit in der beim Aufruf offenen Transaktion gelöscht. Erst beim „Commit“ dieser Transaktion wird der Eintrag tatsächlich gelöscht. Wenn der Eintrag nicht mit gelöscht wird, dann ist dieser beim nächsten Aufruf von „retrieveTransferListEntries“ erneut im Ergebnis.
Die Referenz kann mit der Methode „getReference“ am „TransferListEntry“ abgefragt werden. Wenn das zugehörige Business Entity oder der Dependent die Methode „getReference“ nicht implementiert hat, dann gibt „getReference“ am „TransferListEntry“ einen „Leerstring“ zurück.
Sie können die Referenz mit der Methode „setReference“ am „TransferListEntry“ setzen. Sie können sich mithilfe der Referenz beispielsweise den Fehlercode der letzten erfolglosen Übertragung bzw. einen Zähler für die Anzahl der fehlgeschlagenen Versuche speichern.
Wenn die Übertragungsliste in relativ kurzen Abständen abgefragt wird, dann ist nicht sinnvoll, vor jeder Abfrage eine neue Sequenznummer zu erzeugen. Mithilfe der Methode „existsTransferListEntry“ kann geprüft werden, ob Einträge für ein Business Entity existieren.
Beispiel für das Abfragen der Übertragungsliste
Abfragen und Verarbeiten aller neu geänderten Instanzen des „OrderTransferListControllers“.
CisModificationJournalManager mjm =
env.getModificationJournalManager();
int newSequence = mjm.newTransferListSequence(
env.getDatabaseGuid(),
“com.my.app.general.OrderTransferListController”);
for (
Iterator<CisModificationJournalManager.TransferListEntry>
i =mjm.retrieveTransferListEntries(
“com.my.app.general.OrderTransferListController”,
Order._classGuid, newSequence);
i.hasNext();)
{
CisModificationJournalManager.TransferListEntry entry =
i.next();
tm.beginNew()
try {
Order order = (Order)
om.getObject(entry.getPrimaryKey());
if (order==null) {
System.out.println(
“Order “+entry.getReference()+” deleted”);
}
…
entry.delete();
tm.commit();
} catch (RuntimeException ex) {
tm.rollback();
…
}
}