pátek 13. června 2014

LiveBindings IV - Pokročilejší nastavení II

Uživatelské formátování (Custom Format)

Dalším požadavkem, se kterým se můžeme setkat při propojování vizuálních komponent a dat je potřeba zobrazení dat v určitém požadovaném tvaru. Může se jednat například o prezentaci telefonních či směrovacích čísel, peněžních částek, položek typu datum a podobně.

LiveBindings Methods a Output Converters 

Uvnitř LiveBindings výrazů lze s předávanými parametry pracovat za pomoci dvou skupin vestavěných funkcí. Jedna (Output Converters) sdružuje funkce pro převod datových typů, druhá (Methods) pak především funkce pro zpracování a úpravu předávaných dat.

LiveBindings Konverzní funkce & metody

LiveBindings nabízí řadu vestavěných funkcí. Pokud by funkce kolidovala s funkcí či metodou používanou dotčeným objektem, lze ji deaktivovat. Dialogy pro aktivaci/deaktivaci funkcí a převodníků lze otevřít z "Inspektora Objektů". Na formuláři musí být umístěna a vybrána komponenta "BindingList".

Příklady formátování výstupu

Mějme jednoduchou aplikaci, která bude zobrazovat data z databáze. Struktura databázové tabulky je následující:

CREATE TABLE LB_DEMO2
(
  ID Integer NOT NULL,
  ZBOZI1 Varchar(20),
  ZBOZI2 Varchar(20),
  CENA Numeric(7,2),
  DATUM Date DEFAULT current_date,
  CONSTRAINT PK_LB2 PRIMARY KEY (ID)
);

Formulář pak může vypadat zhruba takto:

Návrh formuláře

Formátování řetězců

Pokud bychom potřebovali běžné formátovací funkce jako je například převod na malá nebo velká písmena, stačí otevřít "Object Inspector" a do "Custom Format" zapsat příslušný předpis s využitím příslušné funkce LiveBindings.

Object Inspector

Výraz "%s" odkazuje na zpracovávaný řetězec v původním tvaru, tak jak byl přijat od zdrojové komponenty.


V praxi může vyvstat potřeba zpracování více než jednoho řetězce. Například budeme požadovat sloučení polí "ZBOZI1" a "ZBOZI2" z naší tabulky a zobrazení výsledného řetězce v komponentě "Label".
Protože LiveBindings engine standardně umožňuje definovat pro vazbu pouze jeden "zdrojový" a jeden "cílový" objekt, musíme použít drobnou lest. V "BindingsList" vytvoříme nový "BindLink". Jako zdrojovou komponentu vybereme "BindSourceDB". Ta reprezentuje datovou sadu (tedy nadřízený objekt), v které jsou obě databázová pole definována. Tím získáme přístup k metodám, které budeme pro manipulaci s daty potřebovat. Nyní již stačí jen zapsat výraz pro sloučení získaných řetězců, např.:

UpperCase(self.FieldByName('ZBOZI1').Text)  + " " +
self.FieldByName('ZBOZI2').Text

BindingsList

"Self" odkazuje na zdrojový objekt a zpřístupňuje tak všechna data ze zdrojového objektu.


Formátování číselných hodnot

Pro formátování čísel můžeme použít dva přístupy. Pokud je zdrojem dat databáze, lze způsob zobrazení určit přímo pro daný sloupec. V okně "Structure" si zobrazíme pro datovou sadu (v naší aplikaci komponenta "Table") všechny sloupce.

Okno "Structure"

Následně označíme sloupec "CENA", pro který hodláme změnit formátování a v okně "Object Inspector" odpovídajícím způsobem nastavíme vlastnost "DisplayFormat". Zde např. "### ###.00".

Nastavení "DisplayFormat"

Stejného výsledku dosáhneme, pokud podobně jako u formátování řetězců nastavíme v okně "Object Inspector" vlastnost "CustomFormat". Formátování nelze nastavit přímo, ale za pomoci funkce "Format()". Pro zobrazení s přesností na dvě desetinná místa tedy například "Format('%%.2f', value)".


Úplný přehled argumentů funkce "Format()" je uveden v Embarcadero docwiki.


Pokud má být spolu s číslem zobrazen další symbol (procenta, měna, apod.), stačí pouze připojit patřičný string. V případě procent je třeba znak uvádět zdvojeně.

Format('%%.2f', value) + ' %%'
Format('%%.2f', value) + ' Kč'


Protože LiveBindings pracuje s ObjectPascalem i C++, lze v předpisu pro formátování použít jak jednoduché tak dvojité apostrofy. Akceptován tak bude zápis Format('%%.2f', value) i Format("%%.2f", value).

Datum a čas

Stejným způsobem je možné formátovat i položky typu datum či čas. Pouze místo funkce "Format()" je třeba použít funkci "FormatDateTime". Zápis formátování pro úplné zobrazení pro položku "DATUM" tak může vypadat následovně:

FormatDateTime('dd/mm/yyyy hh:nn:ss AM/PM', value)

Finální zobrazení

úterý 3. června 2014

LiveBindings III - Custom Parse

V minulých příspěvcích jsem popisoval vizuální návrh propojení za pomoci LiveBindings Designeru nebo průvodce "LiveBindings Wizard". V praxi se však můžeme setkat s požadavky, které vizuálním návrhem nelze jednoduše realizovat.

Uživatelské zpracování (Custom Parse)

Custom Parse umožňuje řešit situace, kdy je třeba data předávaná mezi objekty upravit do formy, kterou je cílový objekt schopen akceptovat. Typicky se jedná o převody mezi různými datovými typy, nebo o úpravu dat do podoby odpovídající definované masce. S tímto požadavkem se můžeme často setkat například při návrhu databázových aplikací v prostředí FireMonkey.

Příklad:

Vytvořme si jednoduchou FireMonkey aplikaci, která bude sloužit k editaci záznamů databázové tabulky s následující strukturou:
CREATE TABLE LBDEMO ( KONTAKT_ID Integer NOT NULL, PRIJMENI Varchar(50), ZEME Char(2), AKTIVNI Integer, CONSTRAINT PK_KONTAKT_ID PRIMARY KEY (KONTAKT_ID) );
Nejprve si v Delphi nebo C++ Builderu navrhneme formulář pro editaci dat (viz obrázek níže). Pole "AKTIVNÍ", které je v databázi reprezentováno jako integer, bude ve formuláři zobrazeno za pomoci komponenty "CheckBox".

Formulář aplikace

Pro propojení jednotlivých komponent se zdrojem dat použijeme LiveBindings Designer (viz následující diagram).

Nastavení LiveBindings

Pokud takový projekt přeložíme, a pokusíme se změnit hodnotu zaškrtnutím nebo naopak odškrtnutím "CheckBoxu", bude zobrazena chyba. Automatická konverze převede typ boolean na string (defaultní datový typ pro všechny výrazy LiveBindings), který databázový stroj očekávající integer nedokáže zpracovat.

Chyba konverze

Řešením je zmiňovaná uživatelská konfigurace propojení. Nejprve v "LiveBindings Designeru" odstraníme nevyhovující vazbu (na symbol vazby klikneme pravým tlačítkem myši) a zvolíme příkaz "Remove Link".

Odstranění vazby

Následně otevřeme "LiveBindings Wizard" a definujeme nový "BindLink". Alternativně jej můžeme vytvořit pomocí dvojkliku na komponentě "BindingsList" (otevře se okno pro editaci LiveBindings propojení), kde z nabídky vybereme volbu "New Binding => BindLink".
V okně "Object Inspektor" nastavíme jako zdrojovou komponentu datový zdroj, v tomto případě "BindSourceDB1". Vlastnost "SourceMemberName" umožňuje vybrat požadovaný sloupec tabulky, zde sloupec s názvem "AKTIVNI". Nakonec určíme komponentu pro zobrazení dat "ControlComponent". Bude jím komponenta "CheckBox1". 

Konfigurace BindLink

Nyní je třeba doplnit výraz "ParseExpressions" pro uložení dat zpět do databáze. To můžeme provést rovněž prostřednictvím "Inspektora Objektů", nebo lépe v okně pro editaci propojení. Výhodou je možnost okamžité validace definovaných výrazů.

Okno BindingsList

V seznamu propojení klikneme dvakrát na v předchozím kroku vytvořený BindLink a do zatím prázdné kolekce přidáme výraz pro "Parse":

Přidání nového výrazu

Do pole pro "Control Expression" vložíme výraz "IfThen(Self.IsChecked, '1', '0')", který vyhodnotí stav komponenty "CheckBox1". Pokud bude zaškrtnuta (vlastnost "IsChecked" bude mít hodnotu "True"), bude výsledkem výrazu text "1", v opačném případě pak text "0". Do pole pro "Source Expression" napíšeme pouze "Text". Datový zdroj je tak informován o tom, že data obdrží jako string a do požadovaného typu (v tomto případě Integer) si je musí převést.

Úprava výrazů

Vyhodnocení vytvořeného výrazu si lze ověřit za pomoci tlačítek "Eval Control" a "Eval Source". Tlačítka "Assign to Control" a "Assign to Source" pak testují vlastní přiřazení hodnoty.

Validace pomocí "Eval Control"

Nyní můžeme aplikaci přeložit a ověřit si, že nyní již LiveBindings hodnotu zadanou prostřednictvím komponenty "CheckBox" interpretují správně a editované záznamy budou korektně uloženy do databáze.


čtvrtek 8. května 2014

Vývoj databázových aplikací VI

Práce s DataSety na straně klienta

Výsledková sada přenesená na klienta může být v řadě případů příliš rozsáhlá, aby se v ní uživatel jednoduše orientoval. FireDAC podobně jako DB Express nebo jeho předchůdce BDE nabízí funkce, které umožňují data třídit, filtrovat je dle určených kritérií nebo v nich vyhledávat.

FireDAC nabízí dvě varianty, jak výše uvedené funkce implementovat. Programátor může využít přímo metody třídy DataSet (či jejích potomků), nebo se i na klientské straně spolehnout na jazyk SQL.

DataSet - Třídění záznamů

Záznamy lze třídit podle jednoho nebo více sloupců, které byly přidány do pojmenovaného "Indexu". Indexů může být definováno více a následně mohou být aktivovány dle potřeby. Záznamy jsou setříděny podle určených sloupců. Pokud není uvedeno jinak, jsou záznamy třízeny vzestupně s ohledem na malá a velká písmena.


Příklad Delphi - Třídění dle indexu

procedure TForm1.SortClick(Sender: TObject);
begin
  // Definování indexu
  FDTable1.Indexes.Clear;
  FDTable1.Indexes.Add();
  FDTable1.Indexes.Items[0].Name := 'idxPrijmeni';
  // Záznamy budou setříděny nejprve podle příjmení a
  // potom podle jména
  FDTable1.Indexes.Items[0].Fields := 'PRIJMENI; JMENO';
  // Sloupec jména bude se bude třídit sestupně
  FDTable1.Indexes.Items[0].DescFields := 'JMENO';
  // Sloupec příjmení se bude třídit bez ohledu na velká
  // a malá písmena
  FDTable1.Indexes.Items[0].CaseInsFields := 'PRIJMENI';
  FDTable1.Indexes.Items[0].Active := True;
  FDTable1.Indexes.Items[0].Selected := True;
end;


Příklad C++ Builder

void __fastcall TForm1::SortClick(TObject *Sender)
{
  FDQuery1->Indexes->Clear();
  FDQuery1->Indexes->Add();
  FDQuery1->Indexes->Items[0]->Name = "idxPrijmeni";
  FDQuery1->Indexes->Items[0]->Fields = "PRIJMENI; JMENO";
  FDQuery1->Indexes->Items[0]->DescFields = "JMENO";
  FDQuery1->Indexes->Items[0]->CaseInsFields = "PRIJMENI";
  FDQuery1->Indexes->Items[0]->Active = True;
  FDQuery1->Indexes->Items[0]->Selected = True;
}

DataSet - Filtrování záznamů

Množinu zobrazovaných záznamů lze omezit použitím vhodného filtru. Filtr může obsahovat logické operátory, operátory LIKE, IN nebo zástupné znaky.


Příklady Delphi

// Osoby s příjmením začínajícím na 'Fa'
procedure TForm1.Filter1Click(Sender: TObject);
begin
  FDTable1.Filtered := False;
  FDTable1.Filter := 'PRIJMENI LIKE ' + QuotedStr('Fa%');
  FDTable1.Filtered := True;
end;

// Výběr výčtem
procedure TForm1.Filter2Click(Sender: TObject);
begin
  FDTable1.Filtered := False;
  FDTable1.Filter := 'OSOBA_ID < 10 AND OSOBA_ID NOT IN (5, 6, 8)';
  FDTable1.Filtered := True;
end;


Příklad C++ Builder

// Filtrování podle rozpětí (osoba_id od 5 do 12)
void __fastcall TForm1::Filter1Click(TObject *Sender)
{
  FDQuery1->IndexFieldNames = "osoba_id";
  FDQuery1->SetRangeStart();
  FDQuery1->KeyExclusive = False;
  FDQuery1->KeyFieldCount = 1;
  FDQuery1->FieldByName("osoba_id")->AsInteger = 5;
  FDQuery1->SetRangeEnd();
  FDQuery1->KeyExclusive = False;
  FDQuery1->KeyFieldCount = 1;
  FDQuery1->FieldByName("osoba_id")->AsInteger = 12;
  FDQuery1->ApplyRange();
}

DataSet -Vyhledání záznamu

Pro vyhledávání v datové sadě je možné použít více přístupů. Nejvíce možností však nabízí funkce "LocateEx". Pokud jsou pro datovou sadu definovány indexy, budou pro vyhledávání použity.


Příklad Delphi

// Vyhledání záznamu pomocí LocateEx
procedure TForm1.FindClick(Sender: TObject);
var
  lxo: TFDDataSetLocateOptions;
begin
  lxo := [lxoPartialKey,lxoCaseInsensitive, lxoFromCurrent];
  //Nalezne postupně první a následující výskyty
  if not FDTable1.LocateEx('PRIJMENI', 'mal', lxo) then
    ShowMessage('Hledaný záznam nebyl nalezen.');
end;



Příklad C++ Builder

void __fastcall TForm1::FindClick(TObject *Sender)
{
  TFDDataSetLocateOptions lxo;
  lxo << lxoPartialKey;
  lxo << lxoCaseInsensitive;
  lxo << lxoFromCurrent;
  if (FDQuery1->LocateEx("Prijmeni", Edit1->Text, lxo) == False)
 ShowMessage("Záznam nenalezen.");
}

Pro Search Options lze použít přepínače:
lxoCaseInsensitive - Hodnoty jsou vyhledány bez ohledu na použití malých nebo velkých písmen
lxoPartialKey - Vyhledávací podmínce budou odpovídat záznamy obsahující hledaný klíč
lxoFromCurrent - Další vyhledávání bude pokračovat od aktuálního záznamu
lxoCheckOnly - Je pouze ověřena existence záznamu splňujícího vyhledávací podmínky. Nedojde ke změně aktuálního záznamu ani spuštění s ní spojených událostí. DataSet zůstává v edit modu
lxoBackward - Vyhledávání bude probíhat od posledního záznamu
lxoNoFetchAll - Pokud je načtena pouze část záznamů, proběhne vyhledání pouze v nich. Nedojde k vynucenému načtení všech záznamů.

Lokální SQL

FireDAC podporuje používání jazyka SQL při práci s datovými sadami na klientské straně. Tuto vlastnost zajišťuje komponenta "FDLocalSQL", která jako databázový engine využívá SQLite. Kromě třídění, filtrování či vyhledávání dat lze za pomoci lokálních SQL dotazů řešit například také požadavky na:  

Heterogenní dotazy - Dotazy, které získávají data z různých databází
In-memory zpracování - Jako datové sady lze použít "TFDMemTable"
Offline zpracování dat - Aplikace pracuje s lokální kopií dat
Migrace dat - Přenesení dat mezi různými databázovými stroji


Příklad Delphi - Filtrování a setřídění záznamů


Vytvoříme aplikaci s komponentami "FDConnection1" připojenou na zvolenou DB a "FDTable1", která získá ze serveru požadovaná data. Do projektu přidáme komponenty "FDConnection2", "FDQuery1, "FDLocalSQL1", "DBGrid1" a "DataSource1".
procedure TForm1.Button1Click(Sender: TObject);
begin
  // Získání DataSetu z DB stroje
  FDConnection1.ConnectionDefName := 'FBDEMODB';
  FDTable1.Connection := FDConnection1;
  FDTable1.TableName := 'OSOBA';
  // Nastavení LocalSQL
  FDConnection2.DriverName := 'SQLite';
  FDLocalSQL1.Connection := FDConnection2;
  FDLocalSQL1.SchemaName := 'Local';
  // DataSety lze přidávat dle potřeby, přičemž mohou být
  // získány z různých DB strojů
  FDLocalSQL1.DataSets.Add(FDTable1, 'Local', 'Osoba');
  FDLocalSQL1.Active := True;
  // Realizace dotazu nad lokálním DataSetem
  FDQuery1.Connection := FDConnection2;
  FDQuery1.Open('select prijmeni, jmeno from osoba where jmeno like' +
  QuotedStr('Fra%') + 'order by prijmeni');
  // Zobrazení dat
  DataSource1.DataSet := FDQuery1;
  DBGrid1.DataSource := DataSource1;
end;



Příklad C++ Builder - heterogenní dotaz



Nastavení komponenty FDLocalSQL

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // DataSet MS SQL Server
  FDConnection1->ConnectionDefName = "MSDEMODB";
  FDTable1->Connection = FDConnection1;
  FDTable1->TableName = "OSOBA";
  // DataSet Firebird
  FDConnection2->ConnectionDefName = "FBDEMODB";
  FDTable2->Connection = FDConnection2;
  FDTable2->TableName = "FIRMA";
  // Nastavení FDLocalSQL
  FDConnection3->DriverName = "SQLite";
  FDLocalSQL1->Connection = FDConnection3;
  FDLocalSQL1->SchemaName = "Local";
  FDLocalSQL1->DataSets->Add(FDTable1, "Local", "Osoba");
  FDLocalSQL1->DataSets->Add(FDTable2, "Local", "Firma");
  // Realizace dotazu nad heterogenními daty
  FDLocalSQL1->Active = True;
  FDQuery1->Connection = FDConnection3;
  FDQuery1->Open("select f.nazev, o.jmeno, o.prijmeni from Osoba o, Firma f where o.firma_id = f.firma_id");
  DataSource1->DataSet = FDQuery1;
  DBGrid1->DataSource = DataSource1;
}

Pro každý databázový stroj včetně SQLite musí aplikace obsahovat příslušnou knihovnu ovladače. V našem případě tedy třídy TFDPhysMSSQLDriverLink, TFDPhysFBDriverLink a TFDPhysSQLiteDriverLink.

středa 23. dubna 2014

Vývoj databázových aplikací V

Uložené procedury

Uložená procedura (Stored Procedure) je podobně jako tabulka nebo pohled databázový objekt, definovaný uvnitř databáze. Místo vlastních dat však obsahují kód (aplikační logiku). Uložené procedury mohou používat téměř libovolné dotazy (DQL) nebo příkazy pro manipulaci s daty (DML). Jejich použití je vhodné zvláště tam, kde by realizace úlohy na klientské straně znamenala zbytečný přenos většího objemu dat ze serveru a následně zpět na server.

Na rozdíl od příkazů DDL pro návrh struktury dat, jejichž převod lze snadno provést pomocí CASE nástrojů, jsou uložené procedury podobně jako spouště (triggery), funkce nebo balíčky obtížněji přenositelné. Dodavatelé aplikací, kteří musí podporovat databázové servery různých dodavatelů, se tak použití uložených procedur často vyhýbají. Bohužel se tak zároveň vzdávají řady výhod, které použití uložených procedur přináší:

Zpřehlednění systému

Uložené procedury zjednodušují a zpřehledňují architekturu systému.
- Změny lze provádět na serveru, aniž by tím byla dotčena aplikace
- Aplikační logiku lze konzistentně sdílet více moduly nebo aplikacemi
- Uložené procedury jsou validovány serverem a mají transparentní vazby
- Uložené procedury se snadno dokumentují
- Návrh uložených procedur lze do velké míry automatizovat  

Sdílení aplikační logiky

Zlepšení odezvy aplikace

Uložené procedury jsou předkompilované a typicky načtené v cache paměti DB serveru. V porovnání se standardním příkazem tak již není nutné ověřovat jejich syntaktickou správnost, provádět kontrolu existence dotčených databázových objektů nebo validovat správnost vazeb. Rovněž odpadá proces optimalizace, kdy se databázový server na základě statistických informací o jednotlivých objektech snaží navrhnout ideální exekuční plán a samozřejmě také vlastní kompilace. Provedení uložené procedury je tak zpravidla výrazně rychlejší než přímé spuštění identické sady příkazů.

Zpracování uložené procedury


Při větších změnách v DB, které by mohli mít dopad na stanovení optimálního exekučního plánu je doporučeno aktualizovat statistiky a provést rekompilaci všech uložených procedur.


Bezpečnost

Použití uložených procedur je mnohem bezpečnější než přímý přístup k datům. Pro každou proceduru je možné přesně stanovit, který uživatel nebo která role má právo ji spustit. Uložená procedura chrání systém před útoky typu SQL injection.

Řízení oprávnění


Uložené procedury je také možné využít ke skrytí aplikační logiky. Po zkompilování uložené procedury lze ze serveru odstranit zdrojový kód (typicky uložený v systémových tabulkách v textové podobě).

Produktivita

V současné době je pro většinu databázových serverů k dispozici široká nabídka nástrojů a utilit, které návrh uložených procedur zjednodušují a automatizují.

Generování uložených procedur

Pro volání uložených procedur databázových strojů Interbase a Firebird, které vracející datovou sadu nelze použít příkaz ExecProc. Místo toho stačí uloženou proceduru nastavit na "Active". Alternativně lze získat data z procedury s využitím FDQuery. Ve vlastnosti "SQL.text" pak provedeme standardní "select", například 'select * from SEL_TEST'.


Příklady Delphi (InterBase/Firebird)

// Načtení záznamů
procedure TForm1.selClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'SEL_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.Active := True;
end;

// Vložení nového záznamu
procedure TForm1.insClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'INS_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.ParamByName('IN_TXT').AsString := Edit1.Text;
  // Zobrazení výstupního parametru
  // v tomto případě ID vloženého záznamu
  ShowMessage(FDStoredProc1.ParamByName('RET').AsString);
  FDStoredProc1.ExecProc();
end;

// Aktualizace záznamu
procedure TForm1.updClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'UPD_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.ParamByName('IN_TXT').AsString := Edit1.Text;
  FDStoredProc1.ParamByName('IN_ID').AsInteger := Edit2.Text;
  FDStoredProc1.ExecProc();
end;

// Odebrání záznamu
procedure TForm1.delClick(Sender: TObject);
begin
  FDStoredProc1.Active := False;
  FDStoredProc1.StoredProcName := 'DEL_TEST';
  FDStoredProc1.Prepare();
  FDStoredProc1.Params.ParamByPosition(1).AsInteger := StrToInt(Edit2.Text);
  FDStoredProc1.ExecProc();
end;


Příklady C++ Builder (Microsoft SQL Server)

// Načtení záznamů
void __fastcall TForm1::selClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_sel_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@eid")->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->Active = True;
}

// Vložení nového záznamu
void __fastcall TForm1::insClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_ins_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@edice")->AsString = Edit2->Text;
  FDStoredProc1->ExecProc();
}

// Aktualizace záznamu
void __fastcall TForm1::updClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_upd_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->ParamByName("@eid")->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->ParamByName("@edice")->AsString = Edit2->Text;
  FDStoredProc1->ExecProc();
}

// Odebrání záznamu
void __fastcall TForm1::delClick(TObject *Sender)
{
  FDStoredProc1->Active = False;
  FDStoredProc1->StoredProcName = "dbo.up_del_edice";
  FDStoredProc1->Prepare();
  FDStoredProc1->Params->ParamByPosition(2)->AsInteger = StrToInt(Edit1->Text);
  FDStoredProc1->ExecProc();
}

Uložené procedury (a další SQL příkazy) lze spouštět na základě událostí na straně serveru, nebo v požadovaných časech. Vhodné např. pro reporting nebo validaci a čištění dat.

sobota 29. března 2014

Vývoj databázových aplikací IV

Práce s datovými sadami na straně klienta

Dosud jsme se zabývali především příkazy, které aplikaci nevracely žádná nebo jen agregovaná data. Častější však bývá situace, kdy je na základě dotazu na klienta přenesena požadovaná množina dat, a uživatelé s nimi provádí další operace. Pro definování datových sad je k dispozici trojice komponent "FDTable", "FDQuery" a "FDStoredProc".

  • FDTable - Vytváří datovou sadu naplněnou záznamy z určené tabulky nebo pohledu
  • FDQuery - Datová sada je definována na základě SQL dotazu
  • FDStoredProc - Datová sada je získána spuštěním uložené procedury

Použití komponenty "FDTable"

Jak již bylo řečeno, datová sada je tvořena množinou záznamů získaných z databáze. Pokud potřebujeme pracovat výhradně se záznamy konkrétní databázové tabulky, je nejsnazší cestou použití komponenty "FDTable". Minimální nastavení vyžaduje specifikovat v inspektorovi objektů nebo v kódu vlastnosti:

  • Connection - určuje DB připojení, které má být použito pro komunikaci s databázovým strojem
  • TableName - Jméno tabulky, která bude zdrojem dat pro naplnění datové sady

V závislosti na typu DB stroje může být nutné zvolit před volbou tabulky odpovídající "Catalog" nebo "Schema", v kterém se požadovaná tabulka nachází.

Nastavení vlastností komponenty FDTable

Parametr názvu připojení pro "FDTable" je doplněn automaticky prostředím. Na to je třeba dát pozor, pokud je na formuláři umístěna více než jedna komponenta "FDConnection".

Použití komponenty "FDQuery"

Pro definování složitějších datových sad slouží komponenta "ADQuery".
Pro pohodlnější zadávání příkazů SQL, případně jejich ověření, nabízí FireDAC jednoduchý QueryEditor. Okno Query Editoru otevřeme tak, že na formulář umístíme komponentu "FDQuery" a klikneme na ni pravým tlačítkem myši. V kontextovém menu pak vybereme volbu "Query Editor...".

Vyvolání Query Editoru

Příkazy se vkládají do dialogu v záložce "SQL Command". Zadat lze i více příkazů současně, je však třeba použít v závislosti na databázovém stroji správný oddělovač příkazů. Jedná-li se o příkazy, které nevracejí jako výsledek sadu záznamů, stačí následně kliknout na tlačítko "Execute" a příkazy budou spuštěny jako dávka. Pokud je mezi zadanými příkazy více než jeden příkaz vracející výsledkovou sadu, zobrazí se pouze první sada záznamů. Pro zobrazení dalších je třeba použít tlačítko "Next RecordSet".

Query Editor

Je-li příkazem vrácena sada záznamů, lze si v záložce "Structure" ve spodní polovině obrazovky zobrazit její vlastnosti (informace o datových typech a atributech jednotlivých sloupců). V záložce "Messages" pak můžeme najít chybová nebo informační hlášení databáze.

Zobrazení informací o datové sadě

Parametrické dotazy
Kromě dávek podporuje FireDAC Query Editor také parametrické dotazy. Do dialogu "SQL Command" vložíme příkaz s požadovanými parametry, například:

select * from osoba where osoba_id = :oid;

Parametrický dotaz

Následně přejdeme do záložky "Parameters", kam Query Editor automaticky doplní jméno použitých parametrů. V dialogu můžeme specifikovat typ parametru, jeho datový typ a případně defaultní hodnotu. Datovým typem může být i pole hodnot. Po zadání hodnoty (nebo indexu pole hodnot) a kliknutí na tlačítko "Execute" se zobrazí výsledek dotazu.

Specifikace a otestování parametrického dotazu

Parametr můžeme následně používat v kódu aplikace, typicky pro zobrazení dat z podřízené tabulky v pohledech "Master/Detail".


Delphi

procedure TForm1.Button1Click(Sender: TObject);
begin
  FDQuery1.Active := False;
  FDQuery1.ParamByName('oid').AsInteger := StrToInt(edit2.Text);
  FDQuery1.Active := True;
end;


C++ Builder

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  FDQuery1->Active = False;
  FDQuery1->ParamByName("oid")->AsInteger = StrToInt(edit2->Text);
  FDQuery1->Active = True;
}

Použití Maker
Query Editor kromě spouštění parametrických dotazů dovoluje testovat i použití maker, nebo makra přímo používat pro modifikaci příkazů. Pokud například chceme spustit příkaz "select" proti několika různým tabulkám, můžeme využít možnost substituce názvů objektů. Do Query Editoru zadáme příkaz v následujícím tvaru:

SELECT &column FROM &table;

V záložce "Macros" jsou automaticky vytvořeny použité proměnné (v tomto případě "column" a "table") a lze jim přiřazovat požadované hodnoty. Po zadání hodnot a kliknutí na tlačítko "Execute" jsou proměnné nahrazeny zadanými hodnotami, příkaz je spuštěn a zobrazena výsledková sada.

Nastavení a otestování makra


Delphi


procedure TForm1.MakroClick(Sender: TObject);
begin
  FDQuery1.Active := false;
  FDQuery1.MacroByName('TABLE').AsRaw := edit2.Text;
  FDQuery1.Active := true;
end;


C++ Builder

void __fastcall TForm1::MakroClick(TObject *Sender)
{
  FDQuery1->Active = False;
  FDQuery1->MacroByName("TABLE").AsRaw = edit2->Text;
  FDQuery1->Active = True;
}

Makra nám mohou pomoci například při vývoji v multiplatformním prostředí, kde se vyskytují databázové stroje různých dodavatelů. Makra obsahují aparát pro podmíněné volání příkazů. FireDAC disponuje vestavěným textovým preprocesorem, který umožňuje používat při sestavování SQL příkazu makra a překlenout tak rozdíly v syntaxi používané různými dodavateli. Příkladem může být rozšíření příkazu "select" pro omezení počtu vrácených záznamů.

Zatímco pro databázový stroj Microsoft SQL je syntaxe následující:

SELECT TOP 5 * FROM firma;

pro dosažení stejného výsledku musíme v DB Interbase použít:

SELECT FIRST 5 * FROM firma;

S použitím FireDAC maker lze tento příkaz zapsat:

SELECT {IF MSSQL} TOP {fi}{IF INTRBASE} FIRST {fi} 5 * FROM firma;

Podpora parametrů a maker musí být aktivní, to znamená příslušné parametry "Command Text Processing" musí být nastaveny na "True".

Nastavení Command Text Processing Options