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

pondělí 17. března 2014

Vývoj databázových aplikací III

Komunikace s DB

Od okamžiku, kdy se úspěšně připojíme k databázi, s ní můžeme začít komunikovat. Databázovému stroji předáváme naše požadavky ve formě SQL příkazů a server na ně odpovídajícím způsobem reaguje.
Z pohledu komponent FireDAC je třeba rozlišovat, jaký typ dat DB server na základě zaslaného požadavku vrátí:
• Příkaz nevrací žádná data (DDL, DML, DCL, TCL)
• Příkaz vrací právě jednu hodnotu (Agregační funkce DQL)
• Příkaz vrací výsledkovou sadu neboli "result set" (DQL)

DDL (Data Definition Language) - Příkazy umožňující definovat datové schéma. Mezi základní příkazy DDL patří:
• CREATE (Vytvoří požadovaný typ objektu)
• ALTER (Umožňuje provést změnu struktury existujícího objektu)
• DROP (Odstraní určený objekt z databáze)

DQL (Data Query Language) - Příkazy pro výběr množiny dat z jedné nebo více tabulek nebo pohledů:
• SELECT (Vrací data z dle zadaných kritérií)

DML (Data Manipulation Language) - Obsahuje set příkazů pro práci s uloženými daty. Mezi základní příkazy DML patří:
• INSERT (Vkládá nové záznamy do určené tabulky)
• UPDATE (Provede aktualizaci určených záznamů)
• DELETE (Odstraní příkazem specifikovaná data)

DCL (Data Control Language) - Příkazy pro řízení přístupových práv k jednotlivým objektům databáze. Mezi příkazy DCL patří:
• GRANT (Přiznává uvedená oprávnění určenému uživateli)
• REVOKE (Odebírá uživateli uvedená oprávnění)

TCL (Transaction Control Language) - Příkazy pro řízení transakcí, nebo li sledu operací sdružených do logických bloků. TCL příkazy jsou:
• COMMIT (Provede uložení v rámci transakce provedených změn)
• ROLLBACK (Odvolává transakci a vrací data do původního stavu)

Komunikace se serverem na úrovni připojení

Některé požadavky lze databázovému serveru předat přímo na úrovni připojení. Výhodou je, že odpadá poměrně vysoká režie, kterou vyžaduje obsluha výsledkových sad. Ta je přímo implementovaná v komponentách jako jsou "FDQuery", "FDTable" nebo "FDStoredProc". Pro zaslání příkazu databázi nabízí FireDAC připojení dvě funkce:

ExecSQL - Použití ExecSQL je vhodné pro příkazy, které nevrací žádnou výsledkovou sadu. Případná chybová hlášení můžeme ošetřit za pomoci bloku "try-except". ExecSQL voláme jako metodu objektu ADConnection:


Delphi

procedure TForm1ButtonClick(Sender: TObject);
begin
  try
    FDConnection1.ExecSQL('REVOKE SELECT ON OBJECT::KRAJ TO public');
  except
    on E: EFDDBEngineException do
      ShowMessage(E.ToString);
  end;
end;



C++ Builder

void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  try {
  FDConnection1->ExecSQL("REVOKE SELECT ON OBJECT::KRAJ TO public");
  } catch (EFDDBEngineException &EFDDBEngineException) {
  ShowMessage(UnicodeString(EADDBEngineException.Message));
  }
}

ExecSQLScalar - Potřebujeme-li spustit příkaz, který vrací právě jednu hodnotu (například agregační funkce jako jsou COUNT, SUM, MAX, AVG a podobně), můžeme použít metodu ExecSQLScalar:


Delphi

procedure TForm1ButtonClick(Sender: TObject);
var
  cnt: integer;
begin
  try
    cnt := FDConnection1.ExecSQLScalar('select count(*) from firma');
  except
    on E: EFDDBEngineException do
      ShowMessage(E.ToString);
  end;
  ShowMessage(IntToStr(cnt));
end;


C++ Builder

void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  Integer cnt;
  try {
  cnt = FDConnection1->ExecSQLScalar("select count(*) from firma");
  ShowMessage(IntToStr(cnt));
  } catch (EFDDBEngineException &EFDDBEngineException) {
  ShowMessage(UnicodeString(EFDDBEngineException.Message));
  }
  ShowMessage(IntToStr(cnt));
}

TFDCommand

Další možností pro spouštění příkazů, které nevrací žádnou výsledkovou sadu je použití komponenty TADCommand. Hlavní předností je přímá podpora asynchronního spouštění příkazů, které lze nastavit za pomoci parametru "CmdExecMode".

Parametr "CmdExecMode" může nabývat následujících hodnot:

amBlocking - Vlákno, z kterého bylo volání příkazu spuštěno, je spolu s uživatelským rozhraním blokováno, dokud není akce dokončena.
amNonBlocking - Vlákno, z kterého bylo volání příkazu spuštěno, je blokováno po dobu provádění příkazu. Uživatelské rozhraní blokováno není.
amCancelDialog  - Nastavení je shodné s volbou "amBlocking", navíc je však zobrazen dialog, který umožňuje spuštěnou akci kdykoliv přerušit.
amAsync – Není blokováno ani vlákno z kterého byl příkaz vyvolán, ani uživatelské rozhraní. Volaná metoda okamžitě předává řízení zpět aplikaci.

Pokud v kódu použijeme parametr "CmdExecMode", musí být součástí projektu instance třídy "TFDGUIxAsyncExecuteDialog". Tu můžeme vytvořit v kódu, nebo ji vybrat v paletě komponent a jednoduše ji umístit na formulář. Zobrazený text lze nastavit přes vlastnost "Prompt".

Dialog Asynchronního spuštění



Delphi

procedure TForm1ButtonClick(Sender: TObject);
begin
  FDCommand1.ResourceOptions.CmdExecMode := amNonBlocking;
  FDCommand1.CommandText.add('REVOKE SELECT ON OBJECT::KRAJ TO public');
  try
    FDCommand1.Active := True;
  except
    on E: EFDDBEngineException do
      ShowMessage(E.ToString);
  end;
end;


C++ Builder

void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  FDCommand1->ResourceOptions->CmdExecMode = amNonBlocking;
  FDCommand1->CommandText->Add("REVOKE SELECT ON OBJECT::KRAJ TO public");
  try {
  FDCommand1->Active = True;
  } catch (EFDDBEngineException &EFDDBEngineException) {
  ShowMessage(UnicodeString(EFDDBEngineException.Message));
  }
}

Pokud má být spuštěna uložená procedura, je třeba nastavit vlastnost "ADCommand.CommandKind" na hodnotu "skStoredProc". Pro ostatní příkazy to není nutné, FireDAC rozpozná typ příkazu automaticky.

Spouštění sady SQL příkazů

Jsou situace, kdy je třeba spustit sled více souvisejících SQL příkazů. Většina databázových strojů má k dispozici "oddělovač příkazů", díky kterému lze "dávku" spustit stejným způsobem, jako samostatný příkaz. Tento přístup však má celou řadu omezení. Pro řešení komplexnějších úlohu lze použít databázový "skript".
Rozdíly mezi Dávkou a skriptem:
• Skript může obsahovat i příkazy jiného jazyka než SQL
• Jsou k dispozici informace o průběhu, snazší logování
• Ve skriptu lze použít všechny typy příkazů včetně DDL
• Skript se na rozdíl od dávky může skládat z více transakcí
• Vykonávání skriptu řídí klient, vykonávání dávky server
• Pro spuštění skriptu je třeba mít patřičné rozhraní

Vytvoření a spuštění dávky
Sestavení dávky z jednotlivých řetězců umožňují komponenty "TADCommand" (metoda CommandText.Add()) nebo "TADQuery" (metoda SQL.Add()).


Delphi

procedure TForm1ButtonClick(Sender: TObject);
begin
  FDQuery1.SQL.Add('Use EMBT');
  FDQuery1.SQL.Add('begin tran');
  FDQuery1.SQL.Add('insert into tb values(1,' + QuotedStr('A') + ');');
  FDQuery1.SQL.Add('insert into tb values(2,' + QuotedStr('B') + ');');
  FDQuery1.SQL.Add('insert into tb values(3,' + QuotedStr('C') + ');');
  FDQuery1.SQL.Add('Commit;');
  FDQuery1.Active := True;
end;


C++ Builder

void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  FDQuery1->SQL->Add("Use EMBT");
  FDQuery1->SQL->Add("begin tran");
  FDQuery1->SQL->Add("insert into tb values(1," + QuotedStr("A") + ');");
  FDQuery1->SQL->Add("insert into tb values(2," + QuotedStr("B") + ');");
  FDQuery1->SQL->Add("insert into tb values(3," + QuotedStr("C") + ');");
  FDQuery1->SQL->Add("Commit;");
  FDQuery1->Active = True;
}

Vytvoření a spuštění SQL Skriptu
SQL Skript lze vytvořit v libovolném textovém editoru. Pokud chceme mít k dispozici pomůcky, jako je třeba kontrola a doplňování syntaxe, formátování nebo vizuální vytváření dotazů, je možné využít některý ze specializovaných komerčních nebo open source editorů.
Jak už bylo uvedeno, skripty používají kromě SQL většinou také další "skriptovací" jazyk. Pro jejich spouštění je tak třeba mít k dispozici odpovídající rozhraní (např. SQLPlus pro Oracle, nebo PowerShell pro MSSQL). Součástí FireDAC je utilita "FDExecutor", kterou lze spustit z příkazového řádku.

FireDAC Executor

Možnost spouštět databázové skripty lze zpřístupnit i uživatelům vytvářených aplikací. Podporu práce se skripty zajišťuje FireDAC komponenta "TFDScript". Komponenta umožňuje sestavit skript řádek po řádku, nebo jej jednoduše načíst z určeného souboru a spustit. Na formulář můžeme také přidat "FDGUIxScriptDialog", který informuje uživatele o průběhu zpracování skriptu nebo zobrazí případné chyby.

FireDAC Script Dialog

Skript lze samozřejmě sestavit také v rámci aplikace. Použijeme třídu "TFDScript". Pomocí jejích metod "SQLScripts.Add" můžeme vytvářet jednotlivé skripty. Obsah skriptu se skládá z jednotlivých položek, které přidáváme pomocí metody "SQL.Add".


Delphi

procedure TForm1ButtonClick(Sender: TObject);
begin
  // Přesměrování výstupů do FDGUIxScriptDialog1
  FDScript1.ScriptDialog := FDGUIxScriptDialog1;
  // Odstraní dříve definované SQL skripty
  FDScript1.SQLScripts.Clear;
  // Přidání nového skriptu do seznamu skriptů
  FDScript1.SQLScripts.Add;
  FDScript1.SQLScripts.Items[0].Name := 'Skript1';
  FDScript1.SQLScripts.Items[0].SQL.Add('use embt;');
  FDScript1.SQLScripts.Items[0].SQL.Add('select * from tb1;');
  // Ověření a spuštění vytvořených skriptů
  FDScript1.ValidateAll();
  FDScript1.ExecuteAll();
end;


C++ Builder

void __fastcall TForm1::ButtonClick(TObject *Sender)
{
  FDScript1->ScriptDialog = FDGUIxScriptDialog1;
  FDScript1->SQLScripts->Clear;
  FDScript1->SQLScripts->Add;
  FDScript1->SQLScripts->Items[0]->Name = "Skript1";
  FDScript1->SQLScripts->Items[0]->SQL->Add("use embt;");
  FDScript1->SQLScripts->Items[0]->SQL->Add("select * from tb1;");
  FDScript1->ValidateAll();
  FDScript1->ExecuteAll();
}

View Petr Houf's LinkedIn profileView Petr Houf's profile