Wprowadzenie
Dziennik zmian jest funkcją dziennika dla historii zmiennych biznesowych i rejestruje zdarzenia dla jednostek biznesowych. Dla każdej jednostki biznesowej i instancji bazy danych można określić, czy zmiany powinny być rejestrowane. Persistence service dba zatem nie tylko o informacje o zmianach dla jednostek biznesowych, ale także o wpisy w dzienniku zmian. Rejestrowane są tylko pomyślnie zakończone transakcje najwyższego poziomu.
Dzięki dziennikowi zmian, m.in. zmiany w danych podstawowych mogą być w pełni odnotowane, a tym samym możliwe do weryfikacji. Dziennik zmian może być aktywowany i przeszukiwany dla dowolnych jednostek biznesowych przy użyciu programowania dostosowującego.
W przeciwieństwie do dziennika zmian, lista transferów rejestruje tylko, że instancja jednostki gospodarczej została zmieniona od czasu ostatniego przetworzenia tej instancji. Lista transferu może być używana w szczególności podczas wymiany danych z systemami zewnętrznymi.
Grupa docelowa
- Deweloper
- Konsultanci techniczni
Opis
Dziennik zmian jest zintegrowany za pośrednictwem interfejsu programistycznego. Aby można było korzystać z dziennika zmian, rejestrowanie zmian dla każdej jednostki gospodarczej musi być aktywowany przez program.
Za każdym razem, gdy zarejestrowana jednostka gospodarcza lub jeden z jego podmiotów zależnych jest zmieniany, wpisy są zapisywane w dzienniku zmian. Istnieją następujące rodzaje wpisów:
- Nowość – gdy tworzony jest nowy obiekt biznesowy, zapisywane są wszystkie atrybuty nowego obiektu.
- Zmiana – gdy obiekt biznesowy jest zmieniany, zmienione atrybuty są zapisywane z ich starymi i nowymi wartościami.
- Usunięcie – po usunięciu obiektu biznesowego zapisywane są wszystkie atrybuty usuniętego obiektu.
Dla każdego wpisu zapisywany jest użytkownik, który go utworzył oraz czas, w którym został utworzony. Oznacza to, że można prześledzić całą historię zmian jednostki gospodarczej.
Przykład rejestrowania dziennika zmian
W przykładzie zamówienie z jedną pozycją jest najpierw wprowadzane przez użytkownika X, następnie kolejna pozycja jest dodawana przez użytkownika Y, w kolejnym kroku zamówienie jest usuwane przez użytkownika X.
Dziennik zmian rejestruje wszystkie dostępy do obiektów (putObject, deleteObject) za pośrednictwem CisObjectManager. Zmiany poprzez putUpdateStatement nie są rejestrowane. UpdateStatements są zwykle używane tylko dla danych istotnych biznesowo podczas reorganizacji. Jeśli tabele są zmieniane bezpośrednio za pomocą narzędzi bazy danych (SQL), zmiany nie są rejestrowane.
Gdy zmiany są wprowadzane i usuwane, stary status zmienionego obiektu biznesowego jest również zapisywany w dzienniku zmian. Oznacza to, że można znaleźć niezarejestrowane zmiany.
Tylko zawartość monitorowanej jednostki biznesowej jest rejestrowana w dzienniku zmian. Jednostka biznesowa zazwyczaj odwołuje się do innych jednostek biznesowych za pośrednictwem ich kluczy technicznych (często identyfikatorów GUID). Jeśli te jednostki biznesowe, do których istnieją odniesienia, nie są monitorowane, mogą zostać w pewnym momencie usunięte. W takim przypadku klucz techniczny nie może być już rozwiązany.
Jeśli monitowana jest jednostka biznesowa, należy również monitorować najważniejsze jednostki biznesowe odwołujące się do tej jednostki. Dotyczy to w szczególności danych podstawowych, takich jak Partnerzy i Artykuły. Jeśli jednostka biznesowa, do którego istnieje odwołanie, zostanie usunięta, można ją również otworzyć za pomocą dziennika zmian.
Jeśli monitoruje się jednostkę biznesową za pomocą dziennika zmian, należy zwrócić uwagę na to, że:
-
dziennik zmian w określonych przypadkach może stać się bardzo duży
-
zapis dziennika zmian powoduje większe obciążenie bazy danych
Dotyczy to w szczególności sytuacji, gdy dziennik zmian zostanie aktywowany dla obiektów biznesowych o bardzo dużej liczbie obiektów lub gdy obiekty biznesowe charakteryzują się bardzo wysoką częstotliwością zmian.
Przed aktywowaniem dziennika zmian należy upewnić się, że system jest w stanie przetworzyć dodatkowe dane w akceptowalnym czasie i przy akceptowalnym zużyciu pamięci.
Interfejs programistyczny
Dziennik modyfikacji może być kontrolowany przez ModificationJournalManager. Z kolei ModificationJournalManager może być odpytywany przez CisEnvironment. ModificationJournalManager zawiera metody aktywacji lub dezaktywacji dziennika zmian dla jednostki biznesowej oraz kwerendy dziennika zmian.
Korzystanie z interfejsu programistycznego ModificationJournal opisano w kolejnych rozdziałach.
Aktywacja dziennika zmian
Dziennik zmian można aktywować dla jednostek biznesowych za pomocą aplikacji Aktywacja dziennika zmian. Ponadto dziennik zmian można również aktywować i dezaktywować za pomocą aplikacji CisModificationJournalManager.
Dziennik zmian jest aktywowany lub dezaktywowany dla jednostki biznesowej w bazie danych przy użyciu metody setActive.
void setActive(byte[] databaseGuid, byte[] classGuid, boolean active);
Metoda isActive
może być użyta do zapytania, czy dziennik zmian dla jednostki biznesowej jest aktywny w bazie danych:
boolean isActive(byte[] databaseGuid, byte[] classGuid);
CisEnvironment env = CisEnvironment.getInstance(); CisModificationJournalManager mjm = env.getModificationJournalManager(); mjm.setActive(env.getDatabaseGuid(),Partner._classGuid,true);
Dynamiczne obiekty biznesowe
Dynamiczne obiekty biznesowe albo rozszerzają istniejącą jednostkę biznesową, albo są samodzielne i nie mają do niej odniesienia. Aby zmiany w dynamicznych obiektach biznesowych były rejestrowane, musi istnieć relacja między instancją dynamicznego obiektu biznesowego a instancją jednostki biznesowej.
Dynamiczne obiekty biznesowe, które zostały utworzone jako rozszerzenia obiektów, takie jak pola zdefiniowane przez użytkownika i charakterystyki artykułów, mają relację z dokładnie jedną jednostką biznesową. Natomiast dynamiczne obiekty biznesowe, które są przywoływane z jednostki biznesowej za pośrednictwem identyfikatora GUID, nie mają tej relacji.
Jeśli dynamiczny obiekt biznesowy nie ma relacji z jednostką biznesową, wówczas za pomocą metody void set_entityKey(byte[] entityTimeDependentKey)
można ustanowić tę relację. Jeśli jednostka biznesowa jest zależna od czasu, należy ustawić czasowy klucz główny w dynamicznym obiekcie biznesowym, w przeciwnym razie należy ustawić normalny klucz główny. W ten sam sposób można również ustawić InstanceString dla dynamicznego obiektu biznesowego:
void set_instanceString(String instanceString)
Klucz jednostki biznesowej i InstanceString muszą być ustawione na instancji dynamicznego obiektu biznesowego, zanim instancja zostanie przesłana do Transaction Manager za pomocą putObject.
Zapytania do dziennika zmian
Dziennik zmian może być przeszukiwany poprzez ModificationJournalManager. Zmiany w dzienniku zmian mogą być podsumowane dla każdej instancji jednostki biznesowej lub każda zmiana może być wyprowadzana indywidualnie.
Zmiany w instancji jednostki biznesowej są zwracane w obiektach klasy JournalEntity.
Obiekt zapytania: JournalQuery
Obiekt klasy JournalQuery może zostać utworzony przez ModificationJournalManager przy użyciu metody createJournalQuery. Klasa JournalQuery zawiera parametry zapytania do dziennika zmian. Aby ograniczyć zapytanie do dziennika zmian do odpowiedniego parametru zapytania, należy użyć nazw stałych. należy użyć klasy com.cisag.pgm.objsearch.SelectionSupport do ustawienia Selection-String dla parametru zapytania.
Zapytanie zmienia się indywidualnie
Jeśli każda zmiana jest odpytywana indywidualnie, JournalEntity jest tworzony dla każdej zmiany i zwracany indywidualnie (zmiany nie są grupowane). Należy otworzyć transakcję na bazie danych, z której ma zostać pobrać dziennik zmian. Następnie można za pomocą metody Następnie można użyć metody
CisIterator retrieveJournalEntities(JournalQuery query);
można pobrać dziennik zmian dla zapytania JournalQuery. Podczas pobierania dziennika zmian transakcja musi pozostać otwarta. Po wysłaniu zapytania do dziennika zmian należy najpierw zamknąć iterator, a następnie, jeśli to konieczne, transakcję.
Zapytanie podsumowujące zmiany
Jeśli zmiany są pobierane w formie podsumowanej, wówczas wszystkie zmiany danej instancji jednostki biznesowej są zapisywane w JournalEntity. Dzięki temu możliwe jest przeprowadzenie szczegółowej analizy historii zmian obiektu. Wadą tego podejścia jest fakt, że pełna instancja jednostki biznesowej wraz ze wszystkimi zmianami jest ładowana do pamięci głównej.
Aby zapytać o dziennik zmian, konieczne jest otwarcie transakcji w bazie danych. Następnie można skorzystać z metody:
Collection retrieveJournalEntities(JournalQuery query);
pozwalającej pobrać dziennik zmian dla określonego zapytania JournalQuery. Po zakończeniu pobierania dziennika zmian należy zamknąć transakcję.
Ze względów historycznych dostępne są jeszcze następujące metody zapytań, jednak nie zaleca się ich stosowania.
Za pomocą metod:
Collection retrieveJournalEntities(byte[] databaseGuid, byte[] key,
Date from, Date until);
Collection retrieveJournalEntities(byte[] databaseGuid, byte[] key,
Date validFrom,
Date from,
Date until);
można pobrać dziennik zmian dla określonej jednostki biznesowej przy użyciu klucza głównego jednostki biznesowej (key) w określonym przedziale czasowym (from <= t < until).
Alternatywnie, dziennik zmian można również pobrać za pomocą InstanceString:
Collection retrieveJournalEntitysByInstanceString(
byte[] databaseGuid, byte[] entityClassGuid,
String instanceSelectionString,
String changeTimeSelectionString,
String entityUpdateTypeSelectionString);
Dostęp do zmian
Dla każdej zmienionej instancji jednostki biznesowej istnieje obiekt klasy JournalEntity, z którego można wyszukiwać wszystkie zmiany w danej instancji. Metody zapytań do dziennika zmian zwracają obiekty tej klasy. Używając metody:
JournalChange[] getChanges();
Czas zmiany i zmieniający się użytkownik mogą być wyszukiwane dla każdej zmiany w jednostce biznesowej. Każdy wpis w zwróconej tablicy identyfikuje jedną zmianę. Indeks w tej tablicy jest następnie wykorzystywany do uzyskania dostępu do odpowiedniej zmiany.
Klasa JournalObject zawiera zmienione wartości obiektu biznesowego. Zmiany mogą być wprowadzane bezpośrednio do obiektu jednostki biznesowej przy użyciu klasy:
JournalObject getEntity();
Zmiany w zależnościach można pobrać za pomocą metody:
JournalObject[] getObjects(byte[] classGuid);
ClassGuids zależności i jednostki biznesowej można sprawdzić za pomocą metody:
CisSet getClassGuids();
Zmiany instancji obiektu biznesowego są zarządzane w obiektach klasy JournalObject. Ścieżki atrybutów obiektu biznesowego mogą być zmieniane za pomocą funkcji:
Set getAttributePaths();
Parts nie są zwracane jako obiekty złożone. Atrybuty Parts są wyzwalane, tj. ścieżki atrybutów do kolumn pierwotnych są określane tak jak w OQL. Wyjątkiem są SpecialParts wymienione w klasie ColumnTyp. Wartość atrybutu jest określana za pomocą ścieżki atrybutu przy użyciu funkcji
ChangedValue getChangedValue(String attributePath,
int changeIndex);
Parametr changeIndex odpowiada indeksowi z metody JournalEntity:getChanges(). W przypadku atrybutów, które nie uległy zmianie w indeksie zmian, zwracana jest wartość null.
Rodzaj zmiany całego obiektu biznesowego jest określany przez metodę:
short[] getUpdateTypes();
Stałe znajdują się w klasie UpdateType i mają następujące znaczenie:
Stała | Opis |
DELETE | Obiekt biznesowy został usunięty podczas zmiany. Zawartość usuniętego obiektu biznesowego znajduje się w starych wartościach atrybutu (Time.OLD). |
INSERT | Obiekt biznesowy został zarejestrowany podczas zmiany. Zawartość nowego obiektu biznesowego jest zawarta w nowych wartościach atrybutu (Time.NEW). |
UPDATE | Obiekt biznesowy został zmieniony podczas modyfikacji. Zmienione atrybuty są dostępne z wartością przed (Time.OLD) i po (Time.OLD) zmianie. |
NOT_MODIFIED | Obiekt nie został zmieniony. Żadne wartości atrybutów nie są dostępne. |
Stare i nowe wartości atrybutu są przechowywane w obiektach klasy ChangedValue. Klasa ta posiada specjalne funkcje zapytań dla każdego typu danych (UpdateType), np:
byte getByte(short time);
W przypadku zapytania o wartość należy określić, czy wymagana jest stara czy nowa wartość (Time). Typ danych (ColumnType) starej lub nowej wartości można określić za pomocą metody:
short getType(short time);
Przykład zapytania do dziennika zmian
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)); … } }
Lista transferowa
Lista transferowa rejestruje, czy jednostka biznesowa lub instancja zależna została utworzona, usunięta lub zmodyfikowana. W przeciwieństwie do dziennika zmian, lista transferów nie rejestruje, jaką zawartość ma instancja obiektu biznesowego ani która z tych treści została zmieniona i w jaki sposób. Podczas rejestrowania listy transferów kontroler CisTransferListController określa, które zmiany są rejestrowane na liście transferów.
Lista transferowa może być tworzona tylko dla obiektów biznesowych, których klucz główny składa się z jednego lub dwóch identyfikatorów GUID.
Należy pamiętać, że lista transferowa monitoruje tylko zmiany w jednostkach biznesowych po aktywacji. Ponadto zmiany za pomocą OQL (Update, Insert lub Delete) nie są rejestrowane przez te listę. W wielu przypadkach konieczne jest zatem ręczne przeprowadzenie pełnej synchronizacji danych, np. z systemem zewnętrznym, niezależnie od listy transferowej.
Rejestrowanie listy transferowej
CisTransferListControlle zarządza rejestrowaniem listy transferów dla jednego lub więcej obiektów biznesowych. Dla każdego przypadku użycia listy transferów, należy zarejestrować pochodną instancję klasy CisTransferListController w hooku com.cisag.pgm.appserver.hook.TransferListRegistryHook definicji hook contract com.cisag.pgm.appserver.Server.
Każdy CisTransferListController jest jednoznacznie identyfikowany przez swój typ. Wszystkie używane typy są zarejestrowane w ValueSet com.cisag.app.general.TransferListType. Jeśli zostanie opracowany nowy CisTransferListController, nowy wpis musi zostać dodany do tego ValueSet.
Alternatywnie, CisTransferListController może być jednoznacznie identyfikowany przez podtyp. Podtyp jest nazwą dowolnego logicznego typu danych, który powinien być używany tylko do rejestracji kontrolera. Pierwotny typ logicznego typu danych nie jest oceniany. Dzięki zastosowaniu podtypu można bezkonfliktowo zarejestrować kontroler listy transferów. Podtypy umożliwiają w szczególności rejestrację kontrolerów listy transferów w aplikacjach.
W konstruktorze CisTransferListController definiowany jest typ lub podtyp oraz obiekty biznesowe monitorowane przez ten kontroler. Dla każdego monitorowanego obiektu biznesowego odpowiednia instancja klasy wewnętrznej CisTransferListController.Entity lub CisTransferListController.Dependent musi zostać przekazana do metody addEntity lub addDependent w konstruktorze.
Metoda isToBeRecorded jednostki biznesowej lub dependent określa, czy zmiana w monitorowanej jednostce biznesowej jest zapisywana na liście transferów. Jeśli metoda ta zwróci wartość:
- true – zmiana zostanie zapisana na liście transferów
- false – zmiana nie jest rejestrowana.
Metoda otrzymuje dwa parametry:
-
old… – stan przed zmianą
-
new… – stan zmieniony w ramach transakcji
Dzięki temu można dokładnie prześledzić, jakie dane zostały zmienione w ramach transakcji.
Jeśli instancja obiektu biznesowego została usunięta, nie można jej już otworzyć podczas analizy listy transferów. Dlatego w liście transferów można zapisać krótką sekwencję znaków jako odniesienie, aby można było zidentyfikować usunięte instancje, np. dla systemów zewnętrznych.
Metoda getReference jednostki biznesowej lub dependent może opcjonalnie zwracać ciąg znaków w celu identyfikacji zmienionego obiektu. Identyfikacja powinna być stała dla obiektu i nie powinna się zmieniać w zależności od statusu obiektu. W większości przypadków klucz biznesowy jest odpowiednią identyfikacją. Metoda jest wywoływana po metodzie isToBeRecorded za każdym razem, gdy zmiana ma zostać zarejestrowana. Nie trzeba zatem ponownie implementować kontroli z metody isToBeRecorded w metodzie getReference. W ramach metod isToBeRecorded i getReference nie wolno zmieniać przeniesionych instancji obiektów biznesowych, ani wykonywać złożonych obliczeń lub dostępu do bazy danych. Ponieważ metoda ta jest wywoływana przy każdej zmianie monitorowanego obiektu biznesowego, czas jej działania może mieć wpływ na wydajność całego systemu. Podczas analizy listy transferów należy przeprowadzać złożone kontrole.
Instancja obiektu biznesowego jest identyfikowana za pomocą klucza głównego. Zależność czasowa nie jest brana pod uwagę na liście transferów. Oznacza to, że podczas odpytywania listy transferów nie jest już możliwe śledzenie, która wersja instancji jednostki biznesowej została zmieniona.
Metoda isActive określa, czy zapis listy transferów dla kontrolera CisTransferListController jest aktywny w bazie danych. Wynik tej metody musi zostać obliczony na podstawie dostosowania bazy danych. Metoda ta jest wywoływana tylko po uruchomieniu serwera aplikacji lub w przypadku zmiany ustawień.
Przykład kontrolera CisTransferListController
Kontroler OrderTransferListController rejestruje utworzenie lub usunięcie instancji jednostki biznesowej Order oraz zmiany atrybutu state.
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; } }
W przypadku użycia podtypu zamiast typu, należy wprowadzić logiczny typ danych com.my.app.general.OrderTransferListController, i zmienić konstruktor kontrolera w następujący sposób:
public OrderTransferListController() { super("com.my.app.general.OrderTransferListController"); addEntity(new OrderEntity()); }
Kontroler można zarejestrować w następujący sposób w implementacji definicji hook contract com.cisag.pgm.appserver.Server:
Class MyTranferListRegistry implements TransferListRegistryHook { public void initialize(TransferListRegistry registry) { registry.add(new OderTransferListController()); } }
Odpytywanie listy transferowej
Lista transferowa może zostać pobrana za pomocą metody retrieveTransferListEntries z klasy CisModificationJournalManagers. Aby uzyskać spójny i stały stan listy transferowej, konieczne jest wygenerowanie nowego numeru sekwencji za pomocą metody newTransferListSequence Wszystkie nowe wpisy na liście transferowej są następnie rejestrowane pod tym numerem. Nowy numer sekwencyjny jest przesyłany podczas odpytywania listy transferowej. Metoda retrieveTransferListEntries zwraca jedynie te wpisy, które zostały zapisane z numerem sekwencji starszym niż przekazany.
Wpisy na liście transferów są reprezentowane przez wewnętrzną klasę CisModificationJournalManager.TransferListEntry. Maksymalnie jeden TransferListEntry jest zwracany dla każdej instancji jednostki biznesowej, niezależnie od tego, jak często był on zmieniany.
Jeśli „TransferListEntry” został pomyślnie przetworzony, zmiana jest usuwana za pomocą metody „delete”. W ten sposób „TransferListEntry” jest usuwany z transakcji, która była otwarta w momencie jego wywołania. Wpis jest faktycznie usuwany tylko wtedy, gdy transakcja zostanie „zatwierdzona”. Jeśli wpis nie zostanie usunięty, pojawi się ponownie w wynikach przy następnym wywołaniu „retrieveTransferListEntries”.
Odniesienie można sprawdzić za pomocą metody „getReference” w „TransferListEntry”. Jeśli powiązana jednostka biznesowa lub jednostka zależna nie zaimplementowała metody „getReference”, wówczas „getReference” zwraca „pusty ciąg” w „TransferListEntry”.
Odniesienie można ustawić za pomocą metody „setReference” na „TransferListEntry”. Na przykład można użyć odniesienia do zapisania kodu błędu ostatniego nieudanego transferu lub licznika liczby nieudanych prób.
Jeśli lista transferów jest odpytywana w stosunkowo krótkich odstępach czasu, nie ma sensu generowanie nowego numeru sekwencji przed każdym zapytaniem. Metoda „existsTransferListEntry” może być użyta do sprawdzenia, czy wpisy istnieją dla podmiotu gospodarczego.
Przykład zapytania o listę transferową
Zapytanie i przetwarzenie wszystkich nowo zmienionych instancji OrderTransferListController.
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(); … } }