|
loarea DBDEMOS,apoi proprietatea TableName la valoarea EVENTS.DB si in
|
səhifə | 10/16 | tarix | 03.01.2019 | ölçüsü | 1,42 Mb. | | #89992 |
| loarea DBDEMOS,apoi proprietatea TableName la valoarea EVENTS.DB si in
final proprietatea Active la valoarea TRUE.In final,selectati obiectul
DBImage1 si setati proprietatea DataSource la valoarea DataSource1 si
proprietatea DataField la valoarea Event_Photo.In acest moment in obiectul
DBImage va fi afisata o imagine.Tastati F9 si utilizati bara de navigatie
pentru a naviga in baza de date.
Pentru a evita stergerea din greseala a unor inregistrari,puteti
selecta obiectul DBNavigator,executati un dublu click pe proprietatea
VisibleButtons si setati False butoanele: nbInsert,nbDelete,nbEdit,nbPost
si nbCancel.
Daca analizati putin exercitiul,observati ca DataSource utilizeaza
obiectul TTable pentru a prelua datele din baza de date iar DBNavigator
si DBImage preiau datele din DataSource1(care este noua sursa de date).
Eventualele modificari efectuate,vor avea efect doar asupra datelor din
DataSource1.Pentru ca modificarile sa devina definitive,este necesara o
alta procedura care sa actualizeze baza de date cu cele din DataSource1.
Pentru a intelege si mai bine mecanismul,puteti sa adaugati si un
obiect TDBGrid (tot din DataControls).Apoi setati prporietatea DataSource
la valoarea DataSource1 si tastati F9.Observati deplasarea in tabel,la
fiecare apasare a butoanelor din DBNavigator.
Deplasati bara de scrol din DBGrid astfel incat sa puteti observa
denumirea fiecarei probe,sau data desfasurarii,pretul biletului,etc.
La nevoie,redimensionati cele doua obiecte,astfel incat sa aveti o imagine
de ansamblu.
Daca optiunea de editare nu este inactivata,selectati una dintre valo-
rile din tabel si introduceti o valoare noua (de exemplu schimbati data
desfasurarii,apoi navigati in tabel si reveniti la valoarea actualizata).
Valoarea se mentine atat timp cat exercitiul este in executie.Daca
inchideti fereastra si relansati programul,datele vor fi afisate cu
valoarea initiala (modificarile au fost temporare si au avut efect doar
asupra tamponului de memorie din DataSource1).
Modificarile din DataSource1 nu pot fi transferate direct asupra
bazei de date.Pentru o astfel de operatie,datele trebuiesc salvate
intr-un tampon de memorie,sau intr-o fila temporara,dupa care se va
utiliza tamponul sau fila temporara pentru a rescrie baza de date.Daca
sunteti incepator,nu este recomandabil sa modificati nici o baza de date
(nici macar cele personale).Pentru inceput,este bine sa exersati cat
mai multe metode de preluare si prelucrare a datelor.Calitatea programa-
torului se reflecta direct asupra modului de selectie a datelor.
-69- DataAccess - TTable
TTable este unul dintre obiectele utilizate pentru a accesa o baza de
date.Se utilizeaza impreuna cu TDataSource,care va prelua datele accesate
de TTable.Un obiect de tip TTable se poate utiliza pentru a prelua un
singur tabel,dintr-o singura baza de date.TTable permite accesul direct
la fiecare inregistrare din tabel.Se poate utiliza pentru baze de date
editate cu unul dintre programele: Paradox,dBASE,Access,FoxPro sau pentru
a prelua date de la un server din retea de tip Interbase,Oracle,Sysbase,
Informix sau DB2.
Prin proprietatile si metodele sale,tTable se poate utiliza si pentru
a prelua doar o parte din date,cu ajutorul unor filtre,care vor selecta
din tabel doar datele care respecta conditiile specificate.
TTable,impreuna cu celelalte componente nonvizuale,poate fi arhivat
intr-un modul de tip DataModule.Un astfel de modul poate contine toate
obiectele necesare pentru a accesa una sau mai multe baze de date.In
cazul in care se vor realiza mai multe aplicatii,care utilizeaza aceleasi
baze de date,modulul DataModule va putea fi utilizat pentru fiecare
dintre acestea.Astfel un proiect poate contine doua sau mai multe file
care utilizeaza acelasi modul,pentru a prelua datele din mai multe tabele.
Pentru a prelua datele din doua sau mai multe tabele,este necesar cate
un obiect TTable si cate un obiect TDataSource,pentru fiecare tabel:
EXEMPLU: (vezi si Prj60)
Salvati o fila noua si proiectul in Prj60.Adaugati din DataControls doua
obiecte TDBGrid si doua obiecte TDBNavigator.
Din File,alegeti New Data Module si adaugati in proiect un modul de
date.Adaugati in DataModule1 doua obiecte TDataSource si doua obiecte
TTable.Apoi selectati fila Form si din meniul File utilizati Use Unit
pentru a introduce modulul de date in lista de unitati a proiectului.
Reveniti in Datamodule1 si setati DataSource1 la DataSet=Table1 si
respectiv DataSource2 la DataSet=Table2.
Alegeti Table1,setati DatabaseName la DBDEMOS,TableName la MASTER.DBF
si Active=TRUE.Alegeti Table2 si setati DatabaseName la DBDEMOS,TableName
la HOLDINGS.DBF si Active la valoarea TRUE.
Selectati DBNavigator1 si setati DataSource la DataModule1.DataSource1.
Selectati DBNavigator2 si setati DataSource la DataModule1.DataSource2.
Selectati DBGrid1 si setati DataSource la DataModule1.DataSource1.
Selectati DBGrid2 si setati DataSource la DataModule2.DataSource2.
Tastati F9 si executati aplicatia.
In fereastra sunt doua tabele care pot fi accesate separat.Fiecare tabel
este prelucrat de o pereche de obiecte DataSource + TTable si afisat de
o pereche de obiecte DBGrid + DBNavigator.
Cele patru obiecte nonvizuale au fost grupate intr-un modul de date.
In viitor,daca doriti sa utilizati cele doua baze de date,intr-o alta
formula (de exemplu sa preluati doar coloanele care contin preturile),
este suficient sa adaugati modulul de date (cu Use Unit din File) in
lista de unitati a noii aplicatii,dupa care puteti selecta obiectele
dorite pentru afisarea datelor.
O alta situatie frecventa,este atunci cand mai multi utilizatori
au preferinte diferite pentru afisarea datelor preluate din aceleasi
baze de date.In acest caz,se poate utiliza un modul de date pentru
preluarea datelor si cate o fila de tip Form,pentru fiecare utilizator
(cu obiectele de prezentare a datelor alese de utilizator).
-70- DataAccess - TQuery
TTable contine si un numar destul de mare de proprietati si metode.
Se pot realiza o serie intreaga de filtre de selectie,sau artificii de
sortare si prezentare a datelor.
Un exemplu simplu: -cautarea unei anumite inregistrari in functie de
o anumita valoare cunoscuta.
EXEMPLU: (vezi si Prj61)
Salvati o fila si proiectul in Prj61.Adaugati in fila un obiect TData-
Source,un obiect TTable,un obiect TDBGrid,un obiect TDBImage si un buton.
Setati DataSet din DataSource1 la valoarea Table1.
Setati Databasename din Table1 la DBDEMOS,TableName la ANIMALS.DBF si
Active la valoarea TRUE.
Setati DataSource din DBGrid1 la DataSource1 si DataSource din DBImage tot
la valoarea DataSource1.
Acum obiectele sunt linkate si tabelul este afisat in DBGrid.Daca dorim
sa adaugam o procedura de cautare rapida,putem utiliza butonul pentru a
introduce si o functie Locate().De exemplu,pentru a cauta prima inregis-
trare in care animalul are dimensiunea de 5 inchi,se va edita urmatoarea
procedura OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
Table1.Locate('SIZE','5',[loCaseInsensitive]);
end;
Tastati F9 si executati aplicatia.Apasand butonul OK,cursorul se va
deplasa automat la prima inregistrare care respecta conditia.
Acest procedeu,este util mai ales atunci cand utilizati foarte
frecvent o baza de date,din ce in ce mai mare,din care doriti sa selectati
doar una sau mai multe inregistrati.Puteti adauga cate un buton de cautare
rapida pentru fiecare inregistrare dorita.
Atentie la metodele care sterg datele din baza de date.TTable lucreaza
direct cu baza de date,astfel incat datele sterse nu pot fi recuperate.
Daca utilizati metode de stegere,este bine ca aceste metode sa fie in-
cluse in proceduri standardizate,verificate cu atentie.Evitati proce-
durile interactive,in care comenzile sunt introduse in timpul executiei,
deoarece un moment de neatentie va poate costa multe ore de munca.
TQuery -este asemanator cu TTable,dar utilizeaza o comanda in limbaj SQL,
pentru accesarea si selectarea datelor dintr-o baza de date.Mai mult decat
atat,comanda SQL permite ca datele sa fie preluate din doua sau mai multe
tabele si sa fie centralizate intr-un obiect de prezentare.Exemple simple
au fost prezentate impreuna cu limbajul SQL(vezi pagina 55) si Prj52.
Formularea comenzii in limbaj SQL,permite o mult mai mare maleabilitate
si selectivitate.Se poate extrage o singura coloana de date,sau chiar o
singura inregistrare,in loc de a afisa intregul tabel.In plus,datele din
doua tabele pot fi combinate,pentru a realiza un tabel comparativ,etc.
TQuery,nu numai ca permite accesara datelor,dar permite si transmiterea
de date,de la component catre baza de date,astfel incat bazele de date
pot fi modificate,actualizate,completate sau chiar sterse definitiv din
memorie.
Obiectul contine si un numar mare de proprietati,metode si evenimente,
care extind si mai mult posibilitatile acestui obiect.Alegeti acest
obiect ori de cate ori este necesara prelucrarea datelor(fie si minimala).
-71-
Pentru comenzile care contin o comanda de tip SELECT (comenzi care nu
modifica baza de date),nu este necesara nici o alta operatie preliminara.
Se introduce comanda in proprietatea SQL si apoi se activeaza setand
proprietatea Active:=True.
Pentru comenzile care contin INSERT,UPDATE sau DELETE (comenzi care
altereaza sau modifica baza de date) este necesara o operatie preliminara
de pregatire a bazei de date.Puteti prepara baza de date cu metoda Pre-
pare,sau puteti apela metoda ExecSql,care executa si o operatie de pre-
parare a bazei de date,inainte de a executa comanda propriu-zisa(continuta
in proprietatea SQL).
Daca nu cunoasteti limbajul SQL,puteti utiliza formula universala:
SELECT * FROM numele bazei de date
care va extrage toate datele din tabelul specificat(vezi si pagina 55).
De exemplu,pentru a selecta si afisa doar doua tipuri de date dintr-un
tabel complex se poate utiliza un exercitiu de genul:
EXEMPLU: (vezi si Prj62)
Salvati si arhivati o fila noua si proiectul,in Prj62.Adaugati un obiect
TDataSource si un obiect TQuery,apoi adaugati din DataControls un obiect
TDBMemo,un obiect TDBImage si un obiect TDBNavigator.
Alegeti DataSource1 si setati DataSet la valoarea Query1.
Alegeti Query1 si setati DatabaseName la valoarea DBDEMOS,apoi introduceti
in SQL urmatoarea comanda: SELECT Notes,Graphic,Category FROM Biolife.db
Apoi setati Active la valoarea True.
Alegeti DBNavigator1 si setati DataSet la valoarea DataSource1.
Alegeti DBMemo1 si setati DataSource=DataSource1 si DataField=Notes.
Alegeti DBImage si setati DataSource=DataSource1 si DataField=Graphic.
Tastati F9 si executati aplicatia.Din tabelul Biolife.db s-au utilizat
doar doua coloane (cele care contin datele utile).
Pentru a identifica rapid o anumita inregistrare,se poate adauga un
buton si o procedura Locate,astfel:
Adaugati un buton si atribuiti urmatoarea procedura OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
Query1.Locate('Category','Shark',[loCaseInsensitive]);
end;
Observati ca am exploatat cea de a treia coloana selectata in comanda SQL.
Apasand butonul,se va selecta automat inregistrarea solicitata.
Puteti realiza un astfel de exercitiu,pentru un album de familie sau
un jurnal de calatorie,o lucrare ilustrata etc.
Pentru a realiza o si mai mare flexibilitate,TQuery poate fi cuplat cu
unul sau mai multe obiecte de tip TUpdateSQL.Obiectele de tip TUpdateSQL
permit schimbarea unei comenzi SQL cu o alta comanda,stergerea sau actua-
lizarea comenzii etc.Acest gen de operatie este utila,mai ales atunci cand
se solicita comenzi de modificare a tabelelor prin operatii de tip DELETE,
INSERT sau UPDATE.In mod normal,o astfel de operatie necestia o etapa de
pregatire si apoi necestia o noua comanda de redeschidere a bazei de date,
dupa ce s-au executat modificarile dorite.Cu ajutorul componentelor de
tip TUpdateSQL,aceste operatii se pot executa in succesiune,astfel incat
pentru utilizator apare ca o operatie simpla de modificare a unei inre-
gistrari.In mod curent,obiectele TQuery si TUpdateSQL necesare pentru
implementarea procedurii se grupeaza intr-un modul de tip DataModule.
-72- DataAccess - TClientDataSet
TClientDataSet este un obiect destinat tot pentru manipularea datelor
si poate fi utilizat atat in aplicatii simple cat si in aplicatii in care
obiectele prezinta mutiple legaturi intre ele (inlantuire multipla).
Poate prelua datele de la un obiect TRemoteServer,caz in care poate fi
sursa de date,pentru obiectele de tip TDataSource,la fel ca si TTable sau
TQuery.Practic,reprezinta o solutie alternativa de preluare a datelor
dintr-o baza de date,mai ales atunci cand baza de date este in conexiune
permanenta cu un obiect de tip TRemoteServer.
Dintre tabelele utilizate pentru exemplificarea componentelor(cele din
Demo/Data) doar tabelul Employee este in conexiune implicita cu obiecte-
le TRemoteServer generate local.Pentru a realiza si alte conexiuni,este
necesara existenta unui server activ.
TClientDataSet,detine un numar foarte mare de metode,care pot fi uti-
lizate pentru a prelua datele dintr-un tabel,pentru a transfera datele
intr-o fila locala,pentru a selecta sau identifica o anumita inregistrare,
pentru a determina numarul sau valoarea inregistrarii curente etc.
O alta utilizare frecventa,este atunci cand o aplicatie solicita mai
multe instante ale unei baze de date.In acest caz,se poate utiliza un
singur TRemoteServer si cate un obiect TClientDataServer pentru fiecare
instanta a tebelului pe care doriti sa o generati.
EXEMPLU: (vezi si Prj63)
Salvati o fila noua si proiectul in Prj63.Adaugati un obiect TDataSource,
un obiect TClientDataSet si un obiect TRemoteServer.
Selectati RemoteServer1 si setati ServerName la valoarea Serv.EmpServer.
Automat,proprietatea ServerGUID va contine un cod de identificare(practic
codul de interfata al tabelului Employee din DBDEMOS).Apoi setati pro-
prietatea Connected la valoarea True.Se va afisa o fereastra denumita
Employee Data Server,care va afisa numarul de clienti si numarul de
solicitari.Din acest moment,aplicatia este in conexiune permanenta cu
tabelul Employee.Daca in calculatorul d-voastra exista un server instalat,
continand baze de date,puteti alege valoarea ServerName dintr-o lista
intreaga de optiuni.
In continuare selectati obiectul ClientDataSet1 si setati proprietatea
RemoteServer la valoarea RemoteServer1.Apoi alegeti proprietatea
ProviderName si setati valoarea EmpQuery si proprietatea Active la valoa-
rea True.
Alegeti DataSource1 si setati DataSet la valoarea ClientDataSet1.
Apoi adaugati un obiect de tip TDBGrid (din DataControls) si setati
proprietatea DataSource la valoarea DataSource1.
Se vor afisa datele din tabelul Employee.
Tastati F9 si executati aplicatia.
Pentru a adauga o noua instanta a tabelului,adaugati un obiect TDataSource
si un obiect TClientDataSet.Alegeti TClientDataSet si setati RemoteServer
la valoarea RemoteServer1 si ProviderName la valoarea EmpQuery.Apoi setati
proprietatea MasterSource la valoarea DataSource1 si executati un dublu
click pe MasterFields.Alegeti FirstName din ambele coloane (Detail Fields
si MasterFields) apoi apasati butonul Add si apoi OK. Setati Active=True.
In acest moment noua instanta a tabelului Employee este legata de prima
instanta,doar pentru inregistrarile in care apare acelasi prenume.
Selectati DataSource2 si setati DataSet la valoarea ClientDataSet2.
Selectati TDBGrid si setati DataSource la valoarea DataSource2.
-73-
Cea de a doua instanta a tabelului contine doar doua inregistrari (cele
pentru care FirstName este identic-adica "Roberto").
Pentru a putea alterna cele doua instante,adaugati doua butoane si
atribuiti cate o procedura OnClick de genul:
DBGrid1.DataSource:=DataSource1 (pentru primul buton)
si respectiv:
DBGrid1.DataSource:=DataSource2 (pentru al doilea buton)
Pentru a adauga o noua instanta,se procedeaza identic.De exemplu,pentru
a adauga o instanta a tabelului cu angajatii care au fost incadrati la
aceeasi data:
Adaugati un obiect TDataSource si un obiect TClientDataSet.
Selectati ClientDataSet3 si setati RemoteServer=RemoteServer1,ProviderName
la valoarea EmpQuery si MasterSource la valoarea DataSource1.
Executati un dublu click pe MasterFields si alegeti in ambele coloane
valoarea HireDate,apoi tastati Add si OK.Setati Active=True.
Selectati DataSource3 si setati DataSet la valoarea ClientDataSet3.
Selectati TDBGrid si setati DataSource la valoarea DataSource3.
Pentru a putea alterna instantele in timpul executiei,adaugati un
nou buton si procedura OnClicK:
procedure tForm1.Button3Click(Sender: TObject);
begin
DBGrid1.DataSource:=DataSource3;
end;
Tastati F9 si executati aplicatia.Puteti utiliza butoanele pentru a
selecta alternativ cele trei instante ale tabelului (toate inregistrarile,
cele cu acelasi prenume si cele din aceeasi data calendaristica).
In mod similar,obiectele de tip TClientDataSet se pot utiliza in diver-
se combinatii,pentru a realiza legaturi multiple intre doua sau mai multe
obiecte.Este preferabil totusi sa alegeti intotdeauna solutia cea mai
simpla,sau sa explicati succint modul si rostul legaturilor realizate (
pentru a simlipica cat mai mult munca celui care va depana programele).
TRemoteServer este un obiect destinat pentru a mentine o conexiune fixa
cu o baza de date,in cadrul unei aplicatii cu legaturi interne multiple.
Singurul RemoteServer implicit este cel care conecteaza tabelul Employee.
Pentru a realiza si alte legaturi fixe,trebuie sa cunoasteti codul de
identificare al interfeetei respective si sa specificati acest cod in
proprietatea ServerGUID (GUID=globally unique identifier).
Codul GUID are o structura de forma:
'{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}' unde x este hexazecimal
TRemoteServer se poate utiliza pentru:
-a realiza o legatura fixa cu o baza de date
-a mentine o lista de obiecte Provider accesibile prin aceasta conexiune
-a obtine o interfata IProvider
-a mentine o lista,ce contine bazele de date accesibile prin RemoteServer
-a intrerupe conexiunea actuala (desface legatura fiza)
Un TRemoteServer poate fi utilizat impreuna cu un numar nelimitat de
obiecte TClientDataSet,pentru a realiza instante diferite ale unui tabel
din baza de date (vezi mai sus exemplul Prj 63).
Metodele se pot utiliza pentru a stabili sau intrerupe conexiunea
(DoConnect si DoDisconnect),etc. Pentru exemplificare vezi Prj63.
-74- DataAccess - TDataBase si TSession
TDatabase este un obiect conceput pentru a realiza o conexiune fixa cu
o baza de date,in special pentru conexiunile cu o baza de date situata
intr-un server de retea,care necesita o parola de acces si un cod de
identificare a utilizatorului.
TDatabase permite controlul conexiunii cu o singura baza de date.Daca
aplicatia utilizeaza mai multe baze de date,este necesar cate un obiect
de tip TDatabase pentru fiecare dintre acestea.Controlul exercitat asupra
conexiunii include utilizarea unei denumiri alias,specificarea unui anumit
driver pentru realizarea conexiunii,control asupra schimburilor de date
(tranzactiilor).In situatiile in care nu se declara in mod explicit un
astfel de obiect,programul va crea un obiect temporar de tip TDatabase,
care va fi utilizat doar in timpul executiei cu un set de proprietati
implicite,suficiente pentru executarea aplicatiei.Puteti declara explicit
un astfel de obiect,ori de cate ori doriti sa alegeti driver-ul de
conexiune sau doriti sa utilizati o denumire alias pentru baza de date.
EXEMPLU: (vezi si Prj64)
Salvati o fila noua si proiectul in Prj64.Adaugati un obiect de tip
TDataSource,un obiect TDatabase,un obiect TTable si un control TDBGrid.
Selectati obiectul TDatabase si setati DatabaseName la valoarea
Reservat.db,apoi AliasName la valoarea DBDEMOS.In final setati proprieta-
tea Connected la valoarea True.
Selectati obiectul Table1 si alegeti DatabaseName.Observati ca in lista
de optiuni a aparut si Reservat.db.Selectati optiunea Reservat.db.Apoi
setati si TableName la valoarea Reservat.db si Active la valoarea True.
Selectati DataSource1 si setati DataSet la valoarea Table1.
Selectati DBGrid1 si setati DataSource la valoarea DataSource1.
Tastati F9 si executati aplicatia.
In situatia in care doriti sa utilizati TDatabase pentru o conexiune cu
un tabel situat intr-un server,de exemplu un tabel de tip FoxPro,va fi
necesar sa setati si proprietatea DriverName,care va solicita numele uti-
lizatorului si parola de acces.Pentru tabele locale,este mai simplu sa
mutati tabelul in directorul Demos/Data.
Observati ca pentru a intrerupe conexiunea se poate utiliza atat pro-
prietatea Active din Table1 cat si proprietatea Connected din Database1.
In plus,obiectul TDatabase contine si proprietatea SessionName.Aceasta
proprietate se poate utiliza impreuna cu obiecte de tip TSession pentru
a crea mai multe instante ale bazei de base.Fiecare instanta va fi reali-
zata cu ajutorul unui obiect de tip TSession.Astfel,baza de date cu care
se realizeaza conexiunea fixa,va putea fi deschisa si redeschisa,in mod
repetat in timpul executiei,utilizand un singur obiect de tip TDatabase.
Fiecare instanta va putea utiliza o comanda SQL diferita (vezi mai jos).
TSession este un obiect destinat pentru a gestiona o linie de executie,
in cadrul aplicatiilor cu comanda multipla (mai multe linii de executie,
sau mai multe instante ale unui obiect).Pentru fiecare aplicatie Delphi
se creaza automat si un obiect TSession implicit.Nu este necesar sa
definiti explicit un astfel de obiect,decat atunci cand proiectati apli-
catii care au mai mult decat o linie de comanda (comenzi pe port paralel,
sau instante multiple).Un obiect de tip TSession exercita un control
global asupra tuturor obiectelor de tip TDatabase conectate la acest
obiect.
-75-
De exemplu,daca doriti sa utilizati mai multe formule de comanda SQL
pentru a deschide o baza de date,aceste comenzi nu vor putea fi executate
concurential.Puteti utiliza cate un obiect TQuery pentru fiecare comanda
si cate un obiect TSession pentru fiecare executie.
O alta situatie posibila este atunci cand utilizati mai multe baze de
date,situate la locatii diferite,pentru a centraliza datele intr-un singur
tabel.In acest caz,puteti utiliza cate un obiect de tip TDatabase pentru
fiecare conexiune si un obiect TSession pentru a lega intre ele obiectele
TDatabase utilizate pentru fiecare comanda.Se pot realiza astfel legaturi
de tip arborescent,care pot gestiona orice situatie logica.
TSession se utilizeaza pentru a conecta intre ele mai multe obiecte
care vor fi utilizate intr-o anumita linie de comanda.Exista trei tipuri
distincte de aplicatii care utilizeaza TSession: standard,multifila si
multicomanda.
Aplicatia standard este cea implicita si se implementeaza automat pentru
orice aplicatie Delphi.
Aplicatia multifila se realizeaza atunci cand sunt necesare mai multe
file,situate la adrese diferite in retea,pentru a centraliza datele intr-o
singura fila locala.
Aplicatia multicomanda se realizeaza atunci cand sunt necesare mai
multe comenzi (uneori simultane) pentru a centraliza datele intr-un
singur tabel local.In acest caz,aplicatia va realiza mai multe conexiuni
paralele cu aceeasi baza de date si va executa comenzile in sesiuni dife-
rite.
EXEMPLU: (vezi si Prj65)
Salvati o fila noua si proiectul in Prj65.Adaugati un obiect TDataSource,
un obiect TDatabase,2 obiecte TSession si 2 obiecte TQuery.
Selectati obiectul Database1 si setati DatabaseName la valoarea Biolife.db
si AliasName la valoarea DBDEMOS.
Selectati Session1 si setati SessionName la valoarea Ses1 si apoi alegeti
Session2 si setati SessionName la valoarea Ses2.
Selectati Query1 si setati DatabaseName la valoarea Biolife.db,SessionName
la valoarea Ses1 si SQL la valoarea: SELECT Category FROM Biolife.db
Selectati Query2 si setati DatabaseName la valoarea Biolife.db,SessionName
la valoarea Ses2 si SQL la valoarea: SELECT * FROM Biolife.db
Apoi adaugati doua butoane,denumite Sesiunea 1 si Sesiunea 2 si atribuiti
urmatoarele proceduri OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
Session2.Active:=False;
Session1.Active:=True;
Database1.SessionName:='Ses1';
Query2.Active:=False;
Query1.Active:=True;
DataSource1.DataSet:=Query1;
end;
Observati ca una dintre liniile de comanda trebuie sa fie inactivata in
timp ce cea de a doua linie de comanda va fi activata.Daca ambele linii
de comanda raman activate in acelasi moment,se creaza o comanda concuren-
tiala care returneaza un mesaj de eroare (una dintre comenzi gaseste
conexiunea "busy" deoarece este ocupata de catre cealalta comanda).
-76-
Adaugati si cea de a doua procedura (butonul al doilea):
procedure TForm1.Button2Click(Sender: TObject);
begin
Session1.Active:=False;
Session2.Active:=True;
Database1.SessionName:='SES2';
Query1.Active:=False;
Query2.Active:=True;
DataSource1.DataSet:=Query2;
end;
Tastati F9 si executati aplicatia.Fiecare din cele doua butoane va
declansa una dintre cele doua sesiuni,respectiv va executa una dintre
cele doua linii de comanda.Obiectul TDatabase nu este strict necesar,dar
a fost adaugat exemplificativ,pentru a ilustra modul de conectare la
doua sesiuni diferite.Nici obiectele TSession nu sunt strict necesare,
deoarece baza de date este locala,dar acesta este modelul de conectare
in cazul in care se utilizeaza mai multe baze de date sau mai multe
comenzi concurentiale.Modul de interconectare al obiectelor face deliciul
fiecarui programator.Este bine sa alegeti intotdeauna solutia cea mai
simpla,sau sa adaugati un minimum de explicatii.Nu uitati ca de cele mai
multe ori,cel care va depana aplicatiile va fi cel care le-a programat.
Este bine ca si asezarea obiectelor in Form sa respecte modul lor de
conectare,astfel incat depanarea lor sa fie cat mai simpla.Atunci cand
utilizati un numar mare de obiecte,este bine sa grupati obiectele in
module,astfel incat depanarea programului sa se reduca doar la depanarea
unui singur modul.Cea mai frecventa situatie care poate afecta un program
functional,este generata de "supradoparea" unui tampon de memorie.In
aceste situatii,este bine ca obiectul,sau modulul din care face parte
sa poata fi inlocuit cat mai usor (este mult mai usor decat sa incercati
sa eliberati tamponul de meorie cu sectoare defecte).
Aplicatiile mari,pot sa utilizeze atat baze de date diferite cat si
comenzi multiple pentru fiecare baza de date.Pentru a simplifica procesul
de depanare,puteti grupa obiectele care executa aceeasi sesiune,sau
obiectele care opereaza asupra aceleiasi baze de date,in module separate.
Calitatea programatorului se poate observa de la prima vedere,dupa modul
de organizare si gestionare a obiectelor si a proceselor aflate in exe-
cutie la un anumit moment dat.Programele de calitate sunt simple,clare,
usor de aplicat si usor de depanat sau modernizat.Este bine sa nu epuizati
toata memoria de operare pentru executarea unei aplicatii.Este bine sa
lasati intotdeauna loc pentru actualizari si modernizari viitoare.
Programele concepute modular,nu numai ca sunt mult mai usor de depanat,
dar pot fi separate in elemente componente.Prin cuplarea unor astfel de
elemente (module) preluate din mai multe programe se poate realiza un
program nou,aproape fara nici un efort.Este bine ca fiecare modul sa
fie conceput cat mai clar si cat mai general,astfel incat sa poata fi
aplicat in programe diferite.In timp,puteti realiza o colectie proprie
de module specializate,astfel incat sa rezolvati orice problema de
programare prin simpla conectare a unui numar de astfel de module.
Pentru programele care urmeaza sa fie utilizate extensiv,este bine sa
arhivati pe langa copia de siguranta si un numar oarecare de module
pregatite pentru depanarea "de urgenta" a programului.
-77- DataAccess - TBatchMove
TBatchMove este un obiect conceput special pentru a copia usor datele
dintr-un tabel in alt tabel.Principala sa utilizare este fie pentru a
copia un tabel dintr-un server de retea la o adresa locala (download),fie
pentru a incarca un tabel de la o adresa locala intr-un server de retea
(upload).Intr-o baza locala,poate face o copie de siguranta automata.
TBatchMode se poate utiliza fie pentru a actualiza doar un anumit grup
de date,fie pentru a copia intregul tabel.
TBatchMode se poate utiliza si in interiorul aceleiasi baze de date.
In aces caz,se poate utiliza pentru a copia un tabel cu un nume nou,
pentru a sterge o parte dintre inregistrarile unui tabel sau pentru a
adauga un set de inregistrari noi.Tipul de operatie efectuat este deter-
minat de proprietatea Mode(batAppend,batCopy,batDelete,batUpdate).
Pentru a specifica grupul de coloane si modificarile efectuate se poate
utiliza proprietatea Mappings.
Tabelul sursa se va specifica in proprietatea Source iar tabelul de desti-
natie va fi setat in proprietatea Destination.
Pentru executarea efectiva a trensferului de date se va apela metoda
Execute.Pentru ca transferul sa poata fi efectuat este necesar ca pro-
prietatile Source,Mode si Destination sa contina valori valide.
Din considerente prectice,este bine sa utilizati pentru specificarea
sursei si a destinatiei obiecte de tip TTable sau TQuery,la care proprie-
tatea Active este setata False (in caz contrar linia de acces la baza de
date va fi identificata ca "busy").
EXEMPLU: (vezi si Prj66)
Salvati o fila noua si proiectul in directorul Prj66.Adaugati un obiect
TDataSource,2 obiecte TTable si un obiect TBatchMove.
Selectati Table1 si setati DatabaseName la valoarea DBDEMOS si TableName
la una dintre valorile optionale (de exemplu VENUES.DB).
Selectati Table2 si setati DatabaseName la valoarea DBDEMOS si TableName
la o valoare oarecare (De exemplu: Tabel2.dbf).Numele introdus va fi
numele bazei de date pe care o realizati copiind tabelul din Table1.
Selectati BatchMove si setati Source=Table1,Destination=Table2 si Mode
la valoarea batCopy.
Pentru a putea vizualiza operatia efectuata,adaugati si un control de tip
TDBGrid.Setati DataSource1 la valoarea DataSet=Table2 si apoi setati in
DBGrid1 DataSource=DataSource1.
Pentru a efectua transferul datelor,adaugati un buton OK si urmatoarea
procedura OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
Table2.Active:=False;
BatchMove1.Execute;
Table2.Active:=True;
end;
Tastati F9 si executati aplicatia.La apasarea butonului OK,datele din
Venues.db vor fi copiate in tabelul Tabel2.dbf nou creat.
Un exemplu complet gasiti si in directorul Help/Examples/Batchmv.
Obiectele de tip TStoredProc si TProvider necesita prezenta unui server
instalat si nu vor face obiectul acestui manual.Sunt destinate pentru a
prelua datele,sau comenzile(proceduri fixe), dintr-un server de retea.
-78- Data Controls - TDBGrid
Paleta de obiecte incluse sub numele de DataControls este formata din
obiecte specializate pentru afisarea datelor (preluate dintr-o baza de
date cu ajutorul obiectelor descrise la paleta DataAccess).Legatura dintre
obiecte,se realizeaza de obicei cu ajutorul unui obiect de tip DataSource.
Obiectele din acest grup,sunt obiecte vizuale si formeaza interfata gra-
fica dintre program si utilizator.In functie de experienta si in functie
de necesitatile de moment,programatorul va alege unul sau mai multe astfel
de obiecte,pentru a prezenta datele,intr-un anumit fel.Pentru tabelele
mici,cu putine date,se vor prefera obiectele care afiseaza intregul tabel.
In cazul tabelelor mari,cu multe coloane si mii de inregistrari,se vor
prefera obiectele care afiseaza cat mai selectiv datele dorite.
TDBGrid este obiectul care permite afisarea si editarea datelor sub
forma de tabel.Este obiectul cel mai frecvent utilizat pentru prezentarea
integrala a tabelelor.Datele sunt preluate de obicei de catre un obiect
TTable sau TQuery si sunt mediate de un obiect TDataSource.
TDBGrid poate fi utilizat si pentru manipularea sau editarea datelor,
sau pentru schimbarea aspectului (culori,fonturi etc.).Prin proprietatea
Fields se poate accesa fiecare obiect de tip TField care formeaza inre-
gistrarile din tabel si implicit si toate proprietatile obiectelor de
tip TField.Prin combinarea acestora,cu proprietatile specifice obiectului
TDBGrid (destul de multe),se obtine o paleta foarte larga de optiuni,care
permit programatorului sa personalizeze modul de prezentare a datelor.
EXEMPLU: (vezi si Prj67)
Salvati o fila noua si proiectul in Prj67.Adaugati un obiect TDataSource,
un obiect TTable si un obiect TDBGrid.
Selectati TTable si setati proprietatea DatabaseName la valoarea DBDEMOS,
TableName la valoarea VENDORS.DB si Active la valoarea True.
Selectati TDataSource si setati DataSet la valoarea Table1.
Selectati TDBGrid si setati DataSource la valoarea DataSource1.
Redimensionati obiectul DBGrid1 astfel incat sa fie la dimensiunile
dorite.Daca doriti sa umple intreaga pagina,pur si simplu setati proprie-
tatea Align la valoarea alClient.
Pentru a schimba aspectul coloanelor,selectati proprietatea Columns
(dublu click) si in fereastra Editing DBGrid1.Columns alegeti butonul
AddAllFields.Apoi selectati pe rand fiecare element(coloana) si utilizati
din ObjectInspector proprietatile Color si Font pentru a selecta paleta
de culori si de fonturi preferate.
Pentru a schimba culoarea cadrului general al tabelului puteti utiliza
proprietatea FixedColor,iar pentru a schimba culoarea paginei in care se
va afisa tabelul puteti utiliza proprietatea Color.
Un set suplimentar de optiuni poate fi accesat executand un dublu
click pe semnul + situat in fata proprietatii Options.
Daca doriti ca tabelul sa fie afisat doar dupa apasarea unui buton,
setati proprietatea Visible la valoarea False si apoi adaugati o procedura
OnClick care seteaza Visible:=True.In acest mod,un set intreg de tabele
poate fi afisat succesiv in aceeasi fereastra de tip utilizator.
Metodele obiectului nu sunt extrem de numeroase,in schimb sunt imple-
mentate un numar destul de mare de evenimente,care permit transmiterea de
mesaje informative despre toate operatiile efectuate in obiect.Cele mai
frecvent utilizate sunt OnCellClick,OnColEnter,OnColExit si OnTitleClick.
Navigarea in tabel poate fi facilitata de un obiect de tip TNavigator.
-79-
Trebuie remarcat faptul ca modificarile datelor realizate in obiectul
TDBGrid nu afecteaza baza de date,ci doar tamponul de memorie in care se
realizeaza reformatarea datelor.Astfel,pentru Tabelul VENDORS.db,puteti
utiliza din Columns butonul Delete pentru a sterge in intregine coloana
Address2 (care nu contine date).Modificarea va fi definitiva doar
pentru aplicatia in care este utilizat tabelul DBGrid1,dar nu va afecta
baza de date originala (daca deschideti tabelul Vendors.db cu un alt
obiect,coloana Address2 este nemodificata).
Ca rezultat,puteti modifica fara frica aspectul tabelelor prezentate,
deoarece datele nu se vor pierde definitiv si puteti reveni oricand la
un format anterior.Pentru ca datele sa fie modificate definitiv,trebuie
fie sa rescrieti fila sursa,fie sa utilizati o comanda SQL.
Alegeti culorile si fonturile preferate.Este bine totusi,ca tabelele sa
nu fie excesiv de colorate.Prea multe culori distrag atentia si obosesc
ochiul.Este bine sa marcati cu culori contrastante doar datele asupra
carora doriti sa atrageti atentia.Eventual,puteti utiliza un efect de
clipire,sau un efect de schimbare alternativa a fondului,pentru datele
pe care doriti sa le evidentiati.
Pentru a prelua din tabel date izolate,puteti utiliza orice combinatie
de proprietati si metode.De exemplu,pentru a prelua datele din celula
de tabel selectata,se poate utiliza urmatoarea procedura:
EXEMPLU: (vezi si Prj68)
Salvati o fila noua si proiectul in Prj68.Adaugati un obiect TDataSource,
un obiect TTable,un obiect TDBGrid si un obiect TLabel.
Setati Table1 la valorile: DatabaseName=DBDEMOS, TableName=COUNTRY.DB si
Active=True.
Setati DataSet din DataSource1 la valoarea Table1.
Setati DataSource din DBGrid1 la valoarea DataSource1.
Apoi alegeti evenimentul OnCellClick si adaugati urmatoarea procedura:
procedure TForm1.DBGrid1CellClick(Column: TColumn);
var text:variant;
begin
text:=DBGrid1.SelectedField.Value;
Label1.Caption:=text;
end;
Selectati Label1 si setati fontul si culoarea dorita,apoi adaugati in
Caption textul: "Textul Selectat: "
Tastati F9 si executati aplicatia.Selectati cateva celule din tabel si
observati ca obiectul TLabel a preluat datele din celula.
In mod similar,puteti edita proceduri care efectueaza diverse operatii
asupra datelor preluate din tabel.Datele pot fi preluate pe linii sau
coloane intregi,pot fi preluate izolat sau pot fi preluate aleator de
catre o functie random,etc.
O aplicatie utila este atunci cand,datele citite selectiv sunt arhi-
vate intr-un tabel sintetic secundar.In acest mod,puteti prelua date
de la un server de retea,dintre care arhivati local doar inregistrarile
care sunt importante pentru utilizator.
Este bine sa conspectati si proprietatile obiectelor TField,care ofera
o paleta destul de larga de expresie si permit un numar destul de mare
de operatii cu datele din fiecare celula a tabelului (mai ales operatii
de schimbare a formatului).
-80- Data Controls - TDBNavigator
TDBNavigator este un obiect format din butoane destinate pentru de-
plasarea cursorului in interiorul bazei de date,sau pentru editarea dete-
lor.Cu ajutorul acestor butoane,utilizatorul se poate deplasa rapid la
inregistrarea dorita,sau poate efectua operatii simple asupra datelor:
adaugarea,actualizarea sau stergerea unei inregistrari.
Butoanele au urmatoarea semnificatie:
First -deplaseaza cursorul la prima inregistrare din tabel
Prior -deplaseaza cursorul la inregistrarea precedenta
Next -deplaseaza cursorul la inregistrarea urmatoare
Last -deplaseaza cursorul la ultima inregistrare din tabel
Insert -insera la pozitia curenta o inregistrare noua
Delete -sterge inregistrarea curenta
Edit -seteaza baza de date in starea Edit(permite scrierea datelor)
Post -scrie in baza de date modificarile efectuate
Cancel -anuleaza modificarile efectuate
Refresh -anuleaza tamponul actual de memorie si creaza un tampon nou
(rescrie datele din tabel intr-un tampon de memorie nou)
TDBNavigator se utilizeaza doar impreuna cu alte controale utilizate
pentru prezentarea datelor(TDBGrid,TDBEdit,TDBImage etc.).
Pentru a controla paleta de butoane afisate,se poate utiliza proprietatea
VisibleButtons.Pentru a exclude un buton setati butonul respectiv False
(Exemplu : nbPost=False).
Butoanele Edit,Post si Delete permit efectuarea de operatii definitive
asupra bazei de date.Utilizati cu atentie aceste butoane.Scrierea datelor
se va efectua doar dupa inchiderea aplicatiei,astfel incat aveti timp
suficient sa reverificati modificarile efectuate.Daca doriti sa utilizati
DBNavigator doar pentru afisarea datelor,este mai bine sa inactivati
aceste butoane pentru a nu fi apasate din greseala.Atunci cand efectuati
modificari frecvente,este bine sa aveti in permanenta si o copie de sigu-
ranta a tabelului initial.
EXEMPLU: (vezi si Prj69)
Salvati o fila noua si proiectul in Prj69.Adaugati un obiect TDataSource,
un obiect TTable,un obiect TDBGrid si un obiect TDBNavigator.
Selectati TTable si setati Databasename=DBDEMOS,TableName=EMPLOYEE.DB si
Active=True.Apoi alegeti DataSource1 si setati DataSet=Table1.
Selectati DBGrid1 si setati DataSource=DataSource1.
Selectati TDBNavigator si setati DataSource=DataSource1
Tastati F9 si executati aplicatia.
Utilizati butoanele pentru a va deplasa in tabel pana la litera numelui
d-voastra,apoi apasati butonul Insert(marcat cu +).In tabel se va include
o inregistrare noua,libera.Introduceti numele,prenumele si datele perso-
nale,apoi apasati butonul Post(marcat cu o pipa) si inchideti aplicatia.
La urmatoarea executie,cu Run,numele d-voastra va figura in tabel,printre
angajatii firmei.Daca doriti sa stergeti intregistrarea,deplasati cursor-
ul la pozitia dorita si apasati butonul Delete(maracat cu -).
Obiectul contine si cateva metode si evenimentele BeforeAction si
OnClick,care pot fi utilizate pentru a adauga noi proceduri sau noi
functionalitati initializate de apasarea unui anumit buton.
Pentru efectuarea operatiilor preliminare se utilizeaza tamponul de memo-
rie din DataSource.Operatia Refresh,nu face decat sa reactualizeze acest
tampon de memorie.Post si Delete actioneaza direct asupra bazei de date.
-81- Data Controls - TDBText
TDBText este un obiect special conceput pentru prezentarea unei singure
celule din tabel(DataField).Este similar cu un obiect TLabel standard,dar
dar utilizeaza un component de tip DataSource pentru preluarea datelor.
Datele afisate nu pot fi modificate (interfata ideala cu utilizatorul).
Datele afisate sunt cele situate la pozitia actuala a cursorului din
DataSource,pentru coloana specificata prin proprietatea DataField.Datele
afisate se modifica o data cu deplasarea cursorului in DataSource.Pentru
deplasare in baza de date,se poate utiliza un TDBNavigator legat de
DataSource.
Obiectele din acest tip se utilizeaza fie pentru a economisi spatiu in
interfata cu utilizatorul,fie pentru a prezenta date din alt tabel decat
cel prezentat in restul obiectelor din interfata (De exemplu,utilizatorul
introduce adresa intr-un tabel de clienti,iar un obiect TDBText afiseaza
simultan,din alta baza de date,codul postal pentru adresa respectiva).
Se pot utiliza astfel de obiecte si pentru a afisa comparativ datele
similare preluate din mai multe tabele (Exemplu: pretul unui produs in
diverse oferte electronice din retea).
Dintre proprietati,pe langa DataSource si DataField,utilizate pentru
preluarea datelor,puteti utiliza proprietatea AutoSize in cazul in care
obiectul va prelua succesiv date cu fonturi diferite.In acest caz,daca
AutoSize este True,obiectul se va redimensiona automat in functie de
atributele datelor preluate.
Metodele nu prezinta facilitati deosebite,in schimb TDBText este
proiectat cu un numar destul de mare de evenimente,care permit o gama
larga de aplicatii: efecte de clipire sau de schimbare a culorii,schim-
barea fonturilor,preluarea automata a datelor,compararea automata a
datelor cu un sablon fix,filtrarea datelor,reformatarea datelor etc.
Cea mai simpla aplicatie,afiseaza selectiv un anumit tip de data:
EXEMPLU: (vezi si Prj70)
Salvati o fila noua si proiectul in Prj70.Adaugati un obiect DataSource
un obiect TTable,un obiect TDBNavigator si 3 obiecte TDBText.
Selectati Table1 si setati Databasename=DBDEMOS,TableName=PARTS.DB si
Active=True.Selectati DataSource1 si setati DataSet=Table1.
Selectati DBNavigator1 si setati DataSource=DataSource1.
Selectati DBText1: -setati DataSource=DataSource1 si DataField=Description
Utilizati Color si Font pentru a obtine aspectul grafic dorit.
Selectati DBText2: -setati DataSource=DataSource1 si DataField=Cost
Alegeti culoarea si fontul dorit.
Selectati DBtext3: -setati DataSource=DataSource1 si DataField=VendorNo
Alegeti Color si Font in functie de preferinte.
Eventual,adaugati cate un camp TLabel care explica ce contine fiecare
obiect DBText.Tastati F9 si executati obiectul.
Din DBNavigator nu sunt utile decat butoanele pentru deplasare.Nu se
pot edita campurile dar se pot utiliza butoanele Insert si Delete.Pentru
a evita stergerea nedorita a unor inregistrari,este mai bine sa inactivati
ultimele patru butoane.
Daca datele sunt preluate dintr-o baza de date mai mare,puteti utiliza
in loc de TTable un obiect TQuery si o formula de comanda SQL cat mai
restrictiva.De exemplu,puteti realiza un filtru care sa excluda toate
inregistrarile mai vechi de o luna.Similar,puteti realiza un filtru care
mascheaza toate datele care nu respecta un anumit format,etc...
-82- Data Controls - TDBEdit
TDBEdit este un obiect similar cu TDBText,dar permite si editarea
datelor afisate.Este conceput pentru aplicatii in care exista o inter-
actiune intre utilizator si baza de date.Se utilizeaza mai ales pentru
a actualiza anumite campuri de date,dintr-un tabel.La fel ca TDBText,
utilizeaza un obiect de tip DataSource pentru a prelua datele si afiseaza
pozitia curenta a cursorului din DataSource,pentru coloana specificata
prin DataField.Navigarea in baza de date se poate face cu TDBNavigator.
TDBEdit afiseaza o singura linie de text,din tabelul selectat in Data-
Source.La fel ca si TMaskEdit,TDBEdit poate utiliza un sablon (o masca)
pentru a forta editarea datelor dupa un anumit format predefinit.
Pe langa proprietatile DataSource si DataField utilizate pentru pre-
luarea datelor,TDBEdit contine si proprietatea ReadOnly care permite sau
interzice editarea datelor afisate.Daca setati proprietatea ReadOnly la
valoarea True,TDBEdit va fi echivalent cu un obiect TDBText.
Metodele (in principal mostenite) si evenimentele nu ofera functio-
nalitati deosebite fata de TDBText.Practic,toate metodele proprii sunt
implicite si nu pot fi apelate direct.
Se prefera acest tip de obiect,atunci cand doriti ca utilizatorul sa
poata efectua modificari asupra datelor din tabel.
EXEMPLU: (vezi si Prj71)
Salvati o fila noua si proiectul in Prj71.Adaugati un obiect TDataSource,
un obiect TTable,un obiect TDBNavigator si 3 obiecte TDBEdit.
Selectati Table1 si setati: DatabaseName=DBDEMOS,Tablename=CUSTOMER.DB si
Active=True.Selectati DataSource1 si setati DataSet=Table1.
Selectati DBNavigator1 si setati DataSource=DataSource1.
Selectati DBEdit1 si setati: DataSource=DataSource1 si DataField=Company.
Alegeti Color si Font dupa preferintele personale.
Selectati DBEdit2 si setati: DataSource=DataSource1 si DataField=Addr1.
Setati preferintele pentru fonturi si culori.
Selectati DBEdit3 si setati DataSource=DataSource1 si DataField=Phone.
Alegeti culorile si fonturile dorite.
Tastati F9 si executati aplicatia.Puteti utiliza butoanele pentru a
naviga in baza de date.Puteti utiliza campurile DBEdit pentru a modifica
dupa bunul plac,datele din tabel.
Daca doriti sa preluati si sa sintetizati intr-o fila separata doar o
Dostları ilə paylaş: |
|
|