Wprowadzenie
Grupa docelowa
- Programiści
Programowanie wirtualnych tabel i funkcji
Wirtualna tabela lub funkcja jest implementowana przez oddzielną klasę. Musi ona wywodzić się z klasy com.cisag.pgm.base.CisODBCVirtualTable lub com.cisag.pgm.base.CisODBCVirtualFunction, które zapewniają podstawową funkcjonalność. Ich metody abstrakcyjne muszą zostać zaimplementowane przez klasę pochodną. Poniżej opisano metody, które należy zaimplementować.
Definiowanie nazwy tabeli
Nazwa wirtualnej tabeli lub funkcji jest definiowana przez logiczny typ danych. Jego ścieżka i nazwa określają nazwę tabeli. Metoda getTablePath() musi zwracać ścieżkę systemu logicznego typu danych.
Typ danych logicznych jest dowolny i nie jest oceniany. Dla logicznego typu danych należy wprowadzić opis danych. Etykieta opisu danych jest używana jako opis tabel. Nazwa tabeli wirtualnej pochodzi od nazwy logicznego typu danych.
public String getTablePath() {
return „com.cisag.app.general:ItemImage.lt”;
}
Ścieżka wirtualnej tabeli jest skracana przez com.cisag., dla systemów od partnera tylko przez com.. Podkreślenie _ zastępuje separator . .
Logiczny typ danych musi być wybrany w taki sposób, aby wynikowa nazwa tabeli nie była używana przez obiekt biznesowy, widok lub inną wirtualną tabelę lub funkcję. Dla lepszej identyfikacji zaleca się użycie w nazwie przyrostka VirtualTable lub VirtualFunction.
Rozwiązywanie konfliktów nazw
Nazwy tabel obiektów biznesowych oraz tabel i funkcji wirtualnych są określane na podstawie nazwy obiektu biznesowego lub logicznego typu danych. Jeśli dla danej nazwy istnieje kilka tabel, zastosowanie ma następujące rozwiązywanie konfliktów:
- jeśli dla danej nazwy istnieje tabela obiektu biznesowego, wówczas używana jest ta tabela. Inne tabele o tej samej nazwie są ignorowane i nie można uzyskać do nich dostępu.
- jeśli dla nazwy istnieje kilka wirtualnych tabel lub funkcji, używana jest pierwsza znaleziona tabela wirtualna. Wszystkie inne tabele są ignorowane.
- jeśli dla danej nazwy istnieje kilka funkcji wirtualnych, używana jest pierwsza znaleziona funkcja wirtualna.
Definiowanie kolumn tabeli
Kolumny tabeli są definiowane w metodzie getAttributes(). Każdy element tablicy definiuje kolumnę tabeli i jej pozycję w tabeli. Element jest ciągiem znaków o następującej strukturze: Nazwa kolumny/ścieżka LDT/typ parametru.
Nazwa kolumny
Nazwa kolumny definiuje nazwę kolumny w tabeli. Nazwa musi być unikalna w obrębie tabeli.
Typ danych
Logiczny typ danych definiuje typ kolumny i etykietę kolumny poprzez powiązany opis danych. Można używać wszystkich podstawowych typów danych systemu (bez SBLOB), a także złożonych logicznych typów danych opartych na następujących specjalnych częściach lub specjalnych logicznych typach danych:
- com.cisag.app.general.obj.DomesticAmount
- com.cisag.app.general.obj.Quantity
- com.cisag.app.general.obj.ForeignAmount
- com.cisag.app.general.obj.Duration
- com.cisag.app.general.obj.PointInTime
- com.cisag.pgm.datatype.CisAttributeTimeStamp
- com.cisag.pgm.datatype.CisAttributeDate
Typ parametru określa, czy kolumna tabeli może być używana jako parametr wejściowy czy wyjściowy.
Parametr wyjściowy
Jeśli kolumna jest parametrem wyjściowym, zwraca obliczoną wartość. Stała klasy OUTPUT_PARAM powinna być określona jako typ parametru w definicji kolumny. Jeśli nie określono typu parametru, kolumna jest zawsze parametrem wyjściowym.
Parametry wejściowe
Parametr wejściowy może być użyty do przekazania wartości do tabeli wirtualnej, która jest używana do obliczenia zwracanej zawartości tabeli. Nazwa kolumny, która może być użyta jako parametr wejściowy, musi zaczynać się od przedrostka „in_” (stała klasy INPUT_PARAM_PREFIX). Parametr wejściowy może być oparty wyłącznie na podstawowych typach danych systemy. Parametr wejściowy jest zawsze opcjonalny i nie musi mieć przypisanej wartości. Po ustawieniu wartości nie można jej już zmienić, tzn. wartość ta jest zwracana jako wartość wyjściowa dla kolumny.
Jeśli kolumna ma być używana tylko jako parametr wejściowy, jako typ parametru należy określić stałą klasy INPUT_PARAM.
Jeśli kolumna ma być używana jako parametr wejściowy lub wyjściowy, jako typ parametru należy określić stałą klasy INPUT_OUTPUT_PARAM. Jeśli kolumnie przypisano wartość, nie można jej zmienić. Jeśli nie ustawiono żadnej wartości, do kolumny można przypisać obliczoną wartość, która jest następnie wyprowadzana.
public String[] getAttributes() { return new String[] { „itemGuid/com.cisag.app.general:OdbcItemGuid.lt/”+ INPUT_PARAM, „description/com.cisag.app.general:OdbcItemDescription.lt/”+ OUTPUT_PARAM, „number/com.cisag.app.general:OdbcItemNumber.lt/”+ OUTPUT_PARAM, „content/com.cisag.app.general:OdbcItemContent.lt/”+ OUTPUT_PARAM }; }
Definiowanie typów baz danych
W przypadku wirtualnej tabeli lub funkcji typy baz danych, w których ma być dostępna, muszą być zdefiniowane w podobny sposób, jak w przypadku obiektu biznesowego. Do tego celu służy metoda getDatabaseContentTypes(). Zwraca ona tablicę z obsługiwanymi typami baz danych. Można określić następujące typy baz danych:
- OLTP
- OLAP
- repozytorium
- konfiguracyjna
Odpowiednie stałe są dostarczane przez klasę com.cisag.pgm.base.
CisODBCExtensionLogic zapewnia odpowiednie stałe.
public short[] getDatabaseContentTypes() {
return new short[] {
CisODBCExtensionLogic.DB_CONTENT_TYPE_OLTP
};
}
Definiowanie uprawnień
Aby ograniczyć dostęp do wirtualnej tabeli lub funkcji, można przypisać autoryzację. Odbywa się to za pomocą metody getBaseBusi-nessObjectGuid(), która zwraca Class-Guid bazowego obiektu biznesowego. Autoryzacje dla bazowego obiektu biznesowego są pobierane z wirtualnej tabeli lub funkcji.
public byte[] getBaseBusinessObjectGuid() {
return CisObjectUtility.getClassGuid(
com.cisag.app.general.obj.Item.class);
}
Ważność instancji klasy
Instancja powiązanej klasy jest tworzona dla każdego zapytania SQL, w którym używana jest wirtualna tabela lub funkcja. Czas życia instancji jest powiązany z czasem życia zapytania SQL. Oznacza to, że zawartość zmiennych instancji nie jest zachowywana między zapytaniami, ale jest dostępna tylko do momentu pełnego obliczenia wyniku zapytania dla wirtualnej tabeli lub funkcji.
Inicjalizacja zmiennych instancji
Wirtualna tabela lub funkcja może posiadać zmienne instancji. Zmienne instancji nie mogą być jednak inicjowane w konstruktorze klasy. W tym celu należy zaimplementować metodę init(), która jest wykonywana przed pierwszym wywołaniem metody run(). W tej metodzie można zainicjować wszystkie zmienne instancji, ponieważ w tym momencie ustawiana jest odpowiednia sesja i kontekst OLTP.
Obliczanie wyniku tabeli
Zawartość tabeli wirtualnej lub funkcji wirtualnej jest obliczana w czasie wykonywania zapytania ODBC. W tym celu system wywołuje metodę run(), w której należy zapisać implementację obliczeń.
Tabela wirtualna
Tabela wirtualna ma typ wykonania, który określa sposób wywoływania metody run() przez system. Deweloper musi zdecydować o typie wykonania podczas implementacji tabeli wirtualnej.
Wykonanie jednorazowe
Jest to standardowy typ wykonania. Metoda run() tabeli wirtualnej jest wywoływana dokładnie raz na zapytanie do bazy danych w celu określenia zawartości tabeli. Wynik jest przechowywany w całości w pamięci głównej, więc ten typ wykonania może być używany tylko wtedy, gdy wynik do obliczenia będzie stosunkowo mały.
Wykonanie w blokach
Klasa implementująca musi nadpisać metodę getExecutionType() i zwrócić stałą CisODBCVirtualTable.TYPE_EXECUTION_BLOCK. Metoda run() tabeli wirtualnej może być wywoływana wielokrotnie dla każdego zapytania bazy danych w celu określenia zawartości tabeli. Każde wywołanie zwraca blok wierszy wynikowych. Ten typ wykonania musi być używany, jeśli wynik do obliczenia może być bardzo duży i dlatego zajmowałby zbyt dużo pamięci głównej.
Wykonanie metody run()
Po wywołaniu metody run() tabela wirtualna otrzymuje obiekt CisODBCVir-tualTableResult, który zawiera wartości parametrów wejściowych i wiersze obliczonego wyniku. Może on zawierać od 0 do n wierszy wyników i jest przekazywany do obiektu wyniku tabeli wiersz po wierszu.
Nowy obiekt wiersza wyniku klasy CisODBCVirtualTableRow jest tworzony za pomocą metody createRow() obiektu wyniku tabeli. Obiekt ten jest kontenerem wartości kolumn wiersza tabeli. Wartości określone w zapytaniu dla użytych parametrów wejściowych są zawsze ustawiane w tym obiekcie. Metoda set() lub get() odpowiadająca typowi kolumny może być użyta do zapytania lub ustawienia wartości poprzez określenie nazwy kolumny. Wartość używanego parametru wejściowego nie może już zostać zmieniona. Metoda isParameterLocked() obiektu CisODBCVirtu-alTableResult może być użyta do zapytania, czy określony parametr wejściowy jest zablokowany, tj. czy została mu przypisana wartość i czy nie można go zmienić.
Jeśli obiekt wiersza wyniku jest wypełniony, jest on dodawany do wyniku za pomocą metody addRow() obiektu wyniku tabeli. Alternatywnie można również dodać kilka obiektów wierszy wyników do wyniku za pomocą metody addAllRows() zamiast kilku wywołań addRow().
Wiersz wyniku jest uwzględniany w wyniku tylko wtedy, gdy został dodany do wyniku za pomocą jednej z tych metod.
Po określeniu wszystkich wierszy wyników należy wywołać metodę setAll-RowsLoaded() na obiekcie wyniku tabeli. Informuje ona system, że obliczanie wyniku tabeli zostało zakończone. Metoda run() nie jest już wtedy wywoływana. Jeśli obiekt wyniku tabeli zostanie zwrócony z 0 wierszami wynikowymi, metoda run() również nie zostanie ponownie wywołana.
Metoda getFetchSize() na obiekcie wyniku tabeli może być użyta do zapytania o liczbę wierszy w bloku wyników wymaganych przez system dla bieżącego wywołania metody run(). Jeśli ten rozmiar jest przestrzegany, wynik jest przesyłany z serwera do klienta przy minimalnej wymaganej liczbie połączeń w obie strony. Rzeczywista liczba wierszy w bloku może jednak odbiegać od pożądanej wartości, a zatem może wymagać większej liczby podróży w obie strony.
Sortowanie wierszy tabeli
Kolejność dodawania odpowiada wyjściowej kolejności wierszy tabeli. Jeśli sortowanie zostało zdefiniowane w instrukcji bazy danych SQL za pomocą klauzuli ORDER-BY, system zwraca wiersze wyników dla tabeli typu pojedyncze wykonanie zgodnie z sortowaniem.
W przypadku tabeli typu wykonanie blokowe za sortowanie odpowiada twórca tabeli wirtualnej. Zwykle taka tabela obsługuje tylko sortowanie stałe, które jest określane przez kolejność dodawania wierszy wyników. Jeśli ma być obsługiwane sortowanie zmienne, sortowanie określone w instrukcji bazy danych SQL można sprawdzić za pomocą metod getSortOrder() i getSort-Directions(). W dokumentacji tabeli wirtualnej deweloper powinien określić, w jaki sposób tabela sortuje wyniki i czy obsługiwane są różne porządki sortowania.
W tym przykładzie tworzony jest obiekt wiersza wyników, w którym sprawdzana jest wartość parametru wejściowego dla kolumny in_itemGuid. Jeśli nie zostanie ustawiona żadna wartość, nie będzie można obliczyć żadnego wyniku, a tabela wirtualna nie zwróci żadnych wierszy wynikowych. public void run(CisODBCVirtualTableResult result) { CisODBCVirtualTableRow resultRow= result.createRow(); byte[] itemGuid= resultRow.getGuid(„in_itemGuid”); if (itemGuid == null) { return; }
Funkcja wirtualna
Metoda run() funkcji wirtualnej jest wywoływana blok po bloku. Przekazany obiekt CisODBCVirtualFunctionResult zawiera wiersze wynikowe bieżącego bloku do wypełnienia, w których ustawiane są odpowiednie wartości parametrów wejściowych. Wartość parametru wejściowego wiersza jest określana przez ocenę klauzuli JOIN powiązanego JOIN. Określa ona kolumnę partnera łączenia, z której parametr wejściowy funkcji wirtualnej uzyskuje swoją wartość. Wiersze wynikowe partnera połączenia zostały wcześniej obliczone w bazie danych. W metodzie run() wartości palet wyjściowych tabeli wirtualnej muszą teraz zostać obliczone dla każdego wiersza wynikowego i przypisane do wiersza wynikowego. Wywołanie jest zatem wykonywane blokowo, aby umożliwić wydajną implementację. Wywołanie może być wykonane kilka razy w zależności od liczby bloków.
Każda funkcja wirtualna używa jednej pamięci podręcznej na zapytanie, która zawiera wiersze wyników. Jeśli wiersz wyniku już istnieje w pamięci podręcznej dla kombinacji wartości parametrów wejściowych, wiersz wyniku nie jest obliczany ponownie, ale zwracana jest zawartość pamięci podręcznej. Dlatego ważne jest, aby wiersz wyniku w zapytaniu zależał tylko od wartości parametrów wejściowych, a nie od poprzednich wierszy wyników.
Wiersz wynikowy jest reprezentowany przez obiekt CisODBCVirtualFunctionRow. Metoda set() lub get() odpowiadająca typowi kolumny może być użyta do zapytania lub ustawienia wartości poprzez określenie nazwy kolumny. Nie można już zmienić wartości przypisanego parametru wejściowego. Metoda isParameterLocked() obiektu CisODBCVirtualFunctionResult może być użyta do zapytania, czy określony parametr wejściowy jest zablokowany, tj. czy została mu przypisana wartość i czy nie można jej zmienić.
Metoda getRowCount() obiektu CisODBCVirtualFunctionResult zwraca liczbę wierszy wynikowych do przetworzenia, metoda getRow() zwraca obiekt CisODBCVirtualFunctionRow dla określonego wiersza wynikowego. Wartości parametrów wejściowych mogą być następnie odpytywane, a wartości kolumn wyjściowych obliczane i umieszczane w obiekcie wiersza wynikowego.
Parametry wejściowe „in_organizationalUnit” i „in_item” są obowiązkowe dla obliczeń. Jeśli żadne wartości nie są ustawione, nie można obliczyć żadnych wartości dla tego wiersza wyników, a proces jest kontynuowany od następnego wiersza wyników. Parametr wejściowy „in_customer” jest opcjonalny i nie musi być ustawiony. Następnie określane są wartości dla kolumn wyjściowych, które są ustawiane w obiekcie wiersza wyników za pomocą metod set() odpowiadających danemu typowi. public void run(CisODBCVirtualFunctionResult virtualFunctionResult) { for(int i= 0,s= virtualFunctionResult.getRowCount(); i<s; i++) { CisODBCVirtualFunctionRow row= virtualFunctionResult.getRow(i); byte[] organisation= row.getGuid(„in_organizationalUnit”); if (organisation == null) continue; byte[] item= row.getGuid(„in_item”); if (item == null) continue; byte[] customer= row.getGuid(„in_customer”); // obliczenie wartości dla kolumn wyjściowych SalesItemData data= Items.retrieveSalesItemData( organisation, item, customer); if (data == null) continue; … // ustawienie wartości kolumn wyjściowych w wierszu wyników row.setGuid(„text”, data.getText()); row.setDecimal(„minMargin”, data.getMinMargin()); … } } Metoda getODBCContext() może być użyta do zapytania o kontekst runtime podczas wykonywania metody run(). Guid aktywnej organizacji, w której kontekście wykonywany jest raport, można określić na obiekcie kontekstu za pomocą metody getOrganisationGuid(). Metoda isMultiSite() może być użyta do określenia, czy aktywna baza danych OLTP działa w środowisku wielostanowiskowym. Metoda getDatabaseGuid() zwraca identyfikator bazy danych, do której odbywa się bieżący dostęp ODBC. Aby wirtualna tabela lub funkcja była dostępna przez ODBC, powiązana klasa musi zostać zarejestrowana. Do tego celu służy hook: com.cisag.pgm.appserver.hook.ODBCRegistryHook, który jest zdefiniowany w Hook Contract: com.cisag.pgm.appserver.Server. Można użyć własnej implementacji tego hook’a, aby zarejestrować wirtualne tabele lub funkcje bez konfliktów. W przypadku kolumny podstawowego typu danych Text możliwa jest automatyczna konwersja zawartości przez serwer ODBC. Jest to obecnie obsługiwane tylko w przypadku danych wyjściowych tekstów HTML. Mogą one zawierać znaczniki HTML, które nie są obsługiwane przez aplikację kliencką ODBC (np. Crystal Reports) i dlatego tekst nie jest wyświetlany zgodnie z oczekiwaniami (np. brak pogrubienia lub kursywy). Aby aktywować automatyczną konwersję dla kolumny typu Text wirtualnej tabeli/funkcji, należy ją zarejestrować. Do tego celu służy hook com.cisag.pgm.appserver.hook.ODBCRegistryHook, który jest zdefiniowany w Hook Contract: com.cisag.pgm.appserver.Server. Można użyć własnej implementacji tego hook’a, aby zarejestrować kolumnę typu danych Text bez konfliktu. Ze względów wydajnościowych automatyczna konwersja powinna być stosowana tylko w przypadku problemów z wyświetlaniem tekstów HTML w aplikacji klienckiej ODBC. Istnieje możliwość dodania kolumny wirtualnej do tabeli obiektu biznesowego, której zawartość jest obliczana w czasie wykonywania zapytania. Taka wirtualna kolumna jest oparta na relacji między obiektem biznesowym a określonym obiektem biznesowym. Dla wirtualnej kolumny relacji musi zostać zaimplementowana oddzielna klasa. Musi ona wywodzić się z klasy com.cisag.pgm.base.CisODBCVirtualColumn, która zapewnia podstawową funkcjonalność. Jej metody abstrakcyjne muszą zostać zaimplementowane przez klasę pochodną. Nazwa kolumny składa się z nazwy atrybutu źródłowego relacji i sufiksu połączonego z podkreśleniem. Typ danych kolumny wirtualnej jest definiowany poprzez logiczny typ danych. Dozwolone są wszystkie podstawowe typy danych systemu (z wyjątkiem BLOB, SBLOB, tekstu). Etykieta opisu danych powiązanego z logicznym typem danych określa etykietę kolumny. Metoda getValueByKey() jest wywoływana w celu obliczenia wartości kolumny relacji wirtualnej dla każdego wiersza wynikowego tabeli obiektu biznesowego. Klucz usługi trwałości, który jest używany do ładowania docelowej instancji obiektu biznesowego, oraz język bazy danych są przesyłane. Klucz usługi trwałości może być następnie użyty do załadowania instancji obiektu biznesowego, biorąc pod uwagę język bazy danych, a wartość zwracana może zostać określona. Podstawowe typy Java muszą zostać zwrócone jako obiekt. Metoda ta jest wywoływana dla każdego wiersza wynikowego źródłowej tabeli obiektów biznesowych. W metodzie getValueByKey() instancja UnitOfMeasure jest ładowana dla przekazanego klucza usługi trwałości, a wartość kodu atrybutu jest zwracana jako wynik. public static class UnitOfMeasureBK extends CisODBCVirtualColumn { private CisEnvironment env= CisEnvironment.getInstance(); private CisTransactionManager tm= env.getTransactionManager(); private CisObjectManager om= env.getObjectManager(); public UnitOfMeasureBK() { super(„BK”, „com.cisag.app.general:UomCode.lt”); } public Object getValueByKey(byte[] key, String language) { byte[] ta = tm.beginNew(CisTransactionManager.OLTP); try { UnitOfMeasure uom= (UnitOfMeasure) om.getObject(key, CisObjectManager.READ, language); return uom != null ? uom.getCode() : null; } finally { tm.rollback(ta); } } } Metoda getODBCContext() może być użyta do zapytania o kontekst runtime podczas wykonywania metody run(). Guid aktywnej organizacji, w której kontekście wykonywany jest raport, można określić na obiekcie kontekstu za pomocą metody getOrganisationGuid(). Metoda isMultiSite() może być użyta do określenia, czy aktywna baza danych OLTP działa w środowisku wielostanowiskowym. Metoda getDatabaseGuid() zwraca identyfikator bazy danych, do której odbywa się bieżący dostęp ODBC. Dla każdego zapytania SQL, w którym używana jest wirtualna kolumna relacji, tworzona jest jedna lub więcej instancji powiązanej klasy, przy czym ustawiana jest odpowiednia sesja i kontekst OLTP. Czas życia instancji jest powiązany z podróżą w obie strony ODBC. Oznacza to, że zawartość zmiennych instancji nie jest zachowywana, a zatem żaden stan nie może zostać zapamiętany. Aby wirtualna kolumna relacji była dostępna za pośrednictwem ODBC, należy zarejestrować powiązaną z nią klasę. Do tego celu służy metoda getVirtualRelati-onshipColumns() klasy com.cisag.app.general.log.CisODBCExtensionLogicImpl. Metoda ta jest wywoływana przez SAS podczas inicjalizacji interfejsu ODBC. Jako parametr otrzymuje w pełni kwalifikowaną nazwę obiektu biznesowego. Dodanie konstrukcji else-if do instrukcji if powoduje dodanie nowej kolumny relacji dla obiektu biznesowego. Jeśli nazwa określonego obiektu biznesowego jest zgodna z nazwą przekazanego obiektu biznesowego, metoda zwraca tablicę z klasami wirtualnych kolumn relacji. public Class[] getVirtualRelationshipColumns(String fullBoName) { if („com.cisag.app.general.obj.UnitOfMeasure”.equals(fullBoName)) { return new Class[]{UnitOfMeasureBK.class}; } … } else if („com.cisag.app.shipping.obj.UnitLoad”.equals(fullBoName)) { return new Class[] {UnitLoadPath.class, UnitLoadSsccPath.class}; } return null; }Określanie kontekstu runtime
Rejestracja wirtualnych tabel lub funkcji
Automatyczna konwersja zawartości kolumny typu danych Text
Programowanie wirtualnych kolumn relacji
Nazwa kolumny
Typ danych
Obliczanie wartości zwracanej
Określanie kontekstu runtime
Ważność instancji klasy
Rejestracja wirtualnej kolumny zależności