Gestiunea Angajatilor DE LA Un Grup DE Firme CU Utilizarea Aplicatiilor Visual Studio C# Si Sql Server

GESTIUNEA ANGAJAȚILOR DE LA UN GRUP DE FIRME CU UTILIZAREA APLICAȚIILOR VISUAL STUDIO C# ȘI SQL SERVER

Capitolul 1. Introducere în bazele de date

Introducere

Datele reprezintă elementul critic al oricărei afaceri. Sunt folosite și stocate practic oriunde, de la afaceri ce încearcă să determine tipare ale consumatorilor bazate pe folosirea cardului de credit, până la agențiile spațiale ce încearcă să adune date de pe alte planete. Informația, datorită importanței sale, necesită un software robust și sigur, capabil să o depoziteze și să o proceseze eficient și rapid. Răspunsul la toate aceste cerințe îl reprezintă o bază de date solidă și sigură.

În acest capitol vom vorbi despre ce reprezintă o bază de date, clasificarea sistemelor de baze de date și despre securitatea și protecția datelor.

Ce este o bază de date?

Încă de la apariția lor, bazele de date au fost printre cele mai cercetate domenii din informatică. O bază de date este un depozit, construit pentru a facilita stocarea eficientă, interogarea și mentenanța datelor. Există mai multe tipuri de baze de date pentru a răspunde numeroaselor cerințe din industrie. O bază de date poate fi specializată în stocarea fișierelor binare, documentelor, imaginilor, videoclipurilor, datelor relaționate, multidimensionale, tranzacționale, analitice sau a datelor geografice, pentru a numi câteva.

Datele pot fi stocate sub diferite forme, cum ar fi tabelar, ierarhic sau grafic. În cazul în care datele sunt stocate sub o formă tabelară, atunci baza de date se numește relațională. Când datele sunt organizate sub o formă arborescentă se numește bază de date ierarhică. Datele stocate sub formă de grafuri reprezentând relațiile dintre obiecte poartă numele de bază de date schematică.

Clasificarea sistemelor de baze de date

Clasificarea după modelul de date

Cel mai răspândit model de date folosit astăzi este modelul de date relațioal. Binecunoscutele sisteme de gestiune a bazelor de date precum Oracle, Microsoft SQL Server și MySQL suportă acest model. Alte modele tradiționale pot fi numite modele ierarhice sau schematice.

Tipuri de modele de date:

Schematic (CODASYL): anii 1970

Hierarhical (IMS): sfârșitul anilor 1960 – începutul anilor 1970

Relațional: anii 1970 și începutul anilor 1980

Entitate–Asociere (Entity–Relationship): anii 1970

Relational Extins: anii 1980

Semantic: sfarșitul anilor 1970 – începutul anilor 1980

Orientat–obiect: sfârșitul anilor 1980 – începutul anilor 1990

Obiect–relațional: sfârșitul anilor 1980 – începutul anilor 1990

Semi–structurat (XML): sfârșitul anilor 1990 până în prezent

Modelul ierarhic

Modelul de date ierarhic organizează datele sub o structură arborescentă., existând o ierarhie de tip părinte–copil. Această structură sugerează faptul că o înregistrare poate avea informații repetitive, în general în segmentul de date „copil”. Datele reprezintă o serie de înregistrări, fiecare înregistrare având asociat câte un set de câmpuri ce conține valori. Modelul grupează toate instanțele specifice înregistrării sub forma unui tip de înregistrări. Aceste tipuri de înregistrări sunt echivalente tabelelor din modelul relațional, înregistrările individuale reprezentând echivalențele rândurilor.

Rădăcina structurii arborescente reprezintă părintele, urmat fiind de nodurile copil. Un nod copil nu poate avea mai mult de un părinte, în schimb un părinte poatea avea mai multe noduri copii.

Această structură este exemplificată în figura 1 – Un model ierarhic.

Figură 1 – Un model ierarhic

Într-un model ierarhic, o colecție de câmpuri ce portă un nume, împreună cu tipurile de date asociate, reprezintă un tip de înregistrare. Fiecare instanță a unui tip de înregistrare este forțat să se supună descrierii indicate în definiția acestuia. Unele câmpuri din tipul de înregistrare reprezintă chei.

Primul sistem de gestiune al unei baze de date ierarhice a fost IMS (Information Management System), creat de către firma IBM în anul 1968. A fost inițial construit drept bază de date pentru programul spațial Apollo ce urma să conducă la aterizarea primilor oameni pe lună. IMS este o bază de date foarte robustă și este încă folosită în zilele noastre de către numeroase companii din toate colțurile lumii.

Modelul schematic sau structural (network data model)

În anul 1969, CODASYL (Committee on Data Systems Languages) și-a lansat prima specificare legată de modelul de date structural. În anii 1971 și 1973 au urmat specificații pentru limbajul de manipulare al datelor „câte–o–înregistrare–pe–rând”. Un exemplu al modelului de date structural CODASYL este ilustrat în figura 2 – Un model schematic.

Figură 2 – Un model schematic

Figura indică tipurile de înregistrări reprezentate de dreptunghiuri. Aceste tipuri de înregistrări pot de asemenea să foloseasca chei pentru a identifica o înregistrare. O colecție de tipuri de înregistrări și chei formează un sistem CODASYL sau o bază de date CODASYL. Observați că un copil (child) poate avea mai mult de un părinte și că fiecare tip de înregistrare poate indica către un altul prin pointeri de tip Next, Prior sau Direct.

Modelul relațional

Modelul de date relațional este unul simplu și elegant. Are o fundație matematică solidă, bazată pe seturi de teorie și calcul de predicate, fiind astfel cel mai folosit model de baze de date în momentul actual.

Unul dintre factorii care au condus la dezvoltarea acestui model a fost reprezentat de marile cantități de timp care se pierdeau de către programatori în momentul în care se realizau schimbări logice sau fizice; de aceea, scopul era să se creeze un model care punea la dispoziție o mai bună independență a datelor. Propunerea cercetătorilor a fost următoarea:

Stochează informația în structuri de date simple (tabele)

Accesul la date se va face prin intermediul unui Data Manipulation Language (DML) – Limbaj de manipulare al datelor

Să fie independent de stocarea fizică

Cu o structură de date simplă, utilizatorul are o șansă mai mare de a dispune de independența logică a datelor. Cu un limbaj de nivel înalt, utilizatorul poate accesa un grad înalt de independență a datelor. Drept urmare, acest model pune la dispoziție și independență față de stocarea fizică. Acestea nu erau posibile în niciunul din modelele anterioare (IMS sau CODASYL).

Figura 3 ilustrează o diagramă Entitate–Asociere (Entity-Relationship) ce reprezintă entitățile (tabelele) împreună cu relațiile asociate unui model relațional simplu. Vom discuta mai multe în legătură cu diagramele Entitarte–Asociere în secțiunea următoare.

Figură 3 – O diagrama entitate-asociere arătând un model relațional simplu

Modelul Entitate–Asociare (Entity–Relationship)

La mijlocul anilor 1970, Peter Chen a propus modelul de date entitate–asociere (E–R). Acesta urma să fie alternativa la modelele de date relaționale, CODASYL și ierarhice. El a propus privirea bazei de date drept o colecție de instanțe și entități. Entitățile sunt obiecte care au o existență independentă de restul entităților existente în baza de date. Acestea conțin atribute, care la rândul lor reprezintă elemente ce caracterizează entitatea. Unul sau mai multe atribute pot fi desemnate drept chei. În cele din urmă, în baza de date pot exista relații între entități. Relațiile pot fi de numeroase tipuri, după cum urmează:

1-to-1

1-to-n

n-to-1

m-to-n

În funcție de entitățile între care se realizază relația, relațiile pot avea și ele atribute ce o descriu.

Figura 4 prezintă un exemplu de diagramă entitate-asociere.

Figura 4 – O diagramă entitate-asociere a unui telefon

În figură, entitățile sunt reprezentate de dreptunghiuri: name, address, voice, fax, și modem. Atributele sunt prezentate sub formă de listă in interiorul fiecărei entități. De exemplu, entitatea voice are atributele vce_num, rec_num, și vce-type. PK reprezintă o cheie primară (Primary Key), iar FK reprezintă o cheie străină (Foreign Key). Conceptul de chei este discutat în detaliu în capitolele următoare.

În loc de a fi folosit ca un model în sine, Entity–Relationship Model și-a găsit succesul drept unealtă de design al bazelor de date relaționale. În plus, procesul de transformare al unei diagrame E–R intr-o colecție de tabele era destul de ușoară, iar tabelele se aflau deja în a 3-a formă normală. Vom discuta despre formele normale în amănunt în secțiunile următoare.

În ziua de astăzi, abilitatea de a crea diagrame entitate–asociere face parte din programele de data–modeling precum IBM InfoSphereTM Data Architect.

Modelul obiect–relațional

Modelul Obiect-Relațional (OR) este foarte similar modelului relațional, însă trateaza fiecare entitate ca pe un obiect (instanță a unei clase), iar relația este o moștenire. Unele dintre beneficiile modelului Obiect–Relațional sunt:

Suportă tipuri complexe definite de utilizator

Moștenirea obiectelor

Obiecte extensibile

Bazele de date Obiect–Relaționale au capacitatea de a stoca relațiile dintre obiecte sub formă relațională.

Alte modele de date

Ultima decadă a fost martora unei cantități substanțiale de muncă acordată studiului modelelor de date semi-structurate, semantice și orientate către obiect.

Limbajul XML este ideal pentru a stoca date semi-structurate. Modelele bazate pe XML au câștigat multă popularitate în industrie datorită arhitecturii orientate-serviciu Web 2.0 (SOA – Service Orientet Arhitecture).

Modelele de date orientate către obiect sunt populare în univesități, dar nu au fost acceptate pe deplin de către industrie; totuși, tool-urile pentru maparea obiect-relațională (object-relational mapping – ORM) sunt disponibile, ceea ce permite integrarea și unificarea programelor orientate-obiect cu bazele relaționale.

Clasificarea după numărul de utilizatori

În funcție de numărul de utilizatori care se pot conecta la o bază de date, avem două categorii:

Baza de date single-user

Baza de date multi-user

Baza de date single-user

Deși bazele de date au fost create cu scopul de a fi accesate de cât mai mulți utilizatori, s-a simțit necesitatea unor baze de date singe-user (sau locale), foarte folositoare programatorilor si arhitecților de baze de date.

Beneficiile unei baze de date single-user constau în viteza de răspuns, deoarece instanța bazei se află la calculatorul de pe care se execută interogarea, astfel, timpul care s-ar fi scurs datorită transmiterii datelor pe rețea nu se pierde. Această bază de date locală se poate transforma într-una multi-user conectând calculatorul la internet și deschizând un port în Firewall pentru a permite conectarea altor calculatoare la aceasta.

Există de asemenea și dezavantaje ale folosirii unei baze de date locale drept una multi-user. De exemplu, calculatorul pe care este instalată v-a fi foarte solicitat din punct de vedere al puterii de calcul (procesorul este componenta principală care este solicitată), astfel, folosirea lui drept stație de lucru poate deveni imposibilă. Un alt dezavantaj poate fi reprezentat de puterea de calcul a stației locale de lucru. Este de la sine înțeles faptul că, un calculator nu beneficiează nici de memoria, nici de puterea unui server performant, astfel, performanța și timpul de acces la baza de date pot fi considerabil scăzute.

Nu se recomandă folosirea bazelor de date single-user decât de către dezvoltatori sau programatori, datorită faptului că aceasta poate fi extrem de performantă și de rapidă, atâta timp cât este folosită de un singur utilizator.

Baza de date multi-user

Spre deosebire de tipul de bază de date single-user, aceasta este destinată folosirii intense de către numeroși clienți, simultan. Acest lucru este posibil datorită serverelor conectate la rețea, ce dispun de o putere mare de procesare și de o memorie dedicată în întregime bazei de date (spre deosebire de un calculator – stație locală – unde memoria era împărțită; astfel bazei de date i se alocă o cantitate mică, de multe ori insuficientă de memorie). Sunt cele mai folosite în momentul acutal,. Modelele bazate pe XML au câștigat multă popularitate în industrie datorită arhitecturii orientate-serviciu Web 2.0 (SOA – Service Orientet Arhitecture).

Modelele de date orientate către obiect sunt populare în univesități, dar nu au fost acceptate pe deplin de către industrie; totuși, tool-urile pentru maparea obiect-relațională (object-relational mapping – ORM) sunt disponibile, ceea ce permite integrarea și unificarea programelor orientate-obiect cu bazele relaționale.

Clasificarea după numărul de utilizatori

În funcție de numărul de utilizatori care se pot conecta la o bază de date, avem două categorii:

Baza de date single-user

Baza de date multi-user

Baza de date single-user

Deși bazele de date au fost create cu scopul de a fi accesate de cât mai mulți utilizatori, s-a simțit necesitatea unor baze de date singe-user (sau locale), foarte folositoare programatorilor si arhitecților de baze de date.

Beneficiile unei baze de date single-user constau în viteza de răspuns, deoarece instanța bazei se află la calculatorul de pe care se execută interogarea, astfel, timpul care s-ar fi scurs datorită transmiterii datelor pe rețea nu se pierde. Această bază de date locală se poate transforma într-una multi-user conectând calculatorul la internet și deschizând un port în Firewall pentru a permite conectarea altor calculatoare la aceasta.

Există de asemenea și dezavantaje ale folosirii unei baze de date locale drept una multi-user. De exemplu, calculatorul pe care este instalată v-a fi foarte solicitat din punct de vedere al puterii de calcul (procesorul este componenta principală care este solicitată), astfel, folosirea lui drept stație de lucru poate deveni imposibilă. Un alt dezavantaj poate fi reprezentat de puterea de calcul a stației locale de lucru. Este de la sine înțeles faptul că, un calculator nu beneficiează nici de memoria, nici de puterea unui server performant, astfel, performanța și timpul de acces la baza de date pot fi considerabil scăzute.

Nu se recomandă folosirea bazelor de date single-user decât de către dezvoltatori sau programatori, datorită faptului că aceasta poate fi extrem de performantă și de rapidă, atâta timp cât este folosită de un singur utilizator.

Baza de date multi-user

Spre deosebire de tipul de bază de date single-user, aceasta este destinată folosirii intense de către numeroși clienți, simultan. Acest lucru este posibil datorită serverelor conectate la rețea, ce dispun de o putere mare de procesare și de o memorie dedicată în întregime bazei de date (spre deosebire de un calculator – stație locală – unde memoria era împărțită; astfel bazei de date i se alocă o cantitate mică, de multe ori insuficientă de memorie). Sunt cele mai folosite în momentul acutal, fiind singurele baze de date productive – destinate clienților.

Există de asemenea și dezavantaje. Faptul că baza de date este dată spre folosință în indrustrie, impune necesitatea de a monitoriza constant activitatea și performanța acesteia. Din punct de vedere al personalului necesar întreținerii unei astfel de baze de date, este mai costisitoare decât baza de date single-user. În cazul de față, un Database Administrator (DBA) monitorizează constant activitatea și traficul pe serverul corespunzător bazei de date multi-user.

Postul de Database Administrator este unul foarte căutat și important în zilele noastre, prin simplul fapt că în spatele fiecărei aplicații complexe, se află cel puțin o bază de date multi-user ce necesită atenția unei persoane avizate.

Clasificarea după numarul de stații pe care este stocată baza de date

În funcție de modul de distribuție al bazei de date, distingem următoarele cazuri:

Bază de date centralizată

Bază de date distribuită

Bază de date distribuită omogen

Bază de date distribuită heterogen

Baza de date centralizată

Figură 5 – Bază de date centralizată

Baza de date centralizată se află stocată pe un singur calculator/server. Oricine dorește să se conecteze la aceasta, trebuie să acceseze aceeași locație.

O bază de date centralizată reprezintă o colecție de informații accesibilă la o singură locație din numeroase puncte, în contrast cu baza de date distribuită, unde informațiile sunt răspândite pe mai multe servere. Există, evident, avantaje și dezavantaje al acestei configurări care pot deveni considerabile când vine vorba de persoanele care iau deciziile cu privire la cum să fie configurată baza de date. Este important să ne gândim la extensibilitatea unei baze de date chiar din momentul în care o configurăm, pentru că în timp cerințele vor crește, iar aceasta trebuie să fie capabilă de a fi ușor adaptată noilor necesități.

Există mai multe modalități de a crea o bază de date centralizată. Există numeroase limbaje de programare capabile să construiască o bază de date, iar companiile pot achiziționa software-ul necesar, în loc să își dezvolte ei propriul soft. Utilizatorii vor avea numeroase căi de a accesa datele, iar baza de date poate fi setată să conțină diferite nivele de securitate, pentru a permite un control sporit al accesului. Personalul de la Tehnologia Informației realizează mentenanța bazei de date folosind numeroase operații, dar în același timp, pot preveni apariția problemelor la fel cum se previne o infecție virală. De asemenea, pot modifica nivelele de acces la cerere și pot administra sistemul de securitate.

Un avantaj al bazei de date centralizate este abilitatea de a accesa întreaga informație într-o singură locație. Căutarea în bazele de date poate fi foarte rapidă, datorită faptului că, motorul de căutare nu trebuie să verifice mai multe locații pentru a furniza rezultatele. Informația poate fi și ea mai usor de organizat într-o singură locație. La un eventual upgrade (necesar datorită cantității prea mari de date) se pot adăuga noi servere la baza de date cu mare ușurință, iar compania nu va trebui să își facă griji referitoare la nevoile unei baze de date distribuite.

O bază de date centralizată este mai ușor de securizat fizic. Poate fi închisă într-o mulțime de feluri pentru a fi protejată de furt, sabotaj, foc, sau alte probleme. Este de asemeni posibilă setarea unui sistem robust de „computer–security” pentru a proteja calculatoarele și rețelele de atacuri cibernetice sau de acces neautorizat. Pentru baze de date cu informații extrem de sensibile, calculatoarele nu vor fi conectate la internet, iar utilizatorii vor trebui să accesese baza de date fizic, pentru a extrage informații. Această situație se poate întâlni la bazele de date guvernamentale, care conțin informații de înaltă importanță.

Figură 7 – Un sistem de baze de date cu o bază de date centralizată

Pe de altă parte, există și dezavantaje. O bază de date centralizată are tendința să sufere blocaje în cazul unui trafic mare de date. Poate fi de asemeni și foarte vulnerabilă în cazul în care se întâmplă ceva și un backup nu a fost realizat în prealabil sau backup-ul este expirat (nu conține versiunea actuală a datelor). Un avantaj al bazelor de date distribuite îl reprezintă factorul de redundanță, care poate permite sistemului să funcționeze individual dacă o bază de date nu functionează.

Baza de date distribuită

O bază de date distribuită, ce face parte dintr-un sistem, este stocată/răspândită fizic pe mai multe calculatoare sau servere, în locații diferite, toate fiind interconectate prin intermediul unui „data communication network” (rețele de comunicare a datelor). Ele pot fi răspândite în WAN (Wide Area Network – rețea de arie largă) sau LAN (Local Area Network – rețea locală). Calculatoarele pot fi de tipuri diferite, precum IBM Mainframes, VAX-uri, stații de lucru SUN, PC-uri și asa mai departe, acestea având posibilitatea de a rula sisteme de operare diferite, iar fiecare dintre segmentele de date pot fi gestionate de sisteme diferite de gestiune a bazelor de date precum Oracle, Ingress sau Microsoft SQL Server. Sistemele de gestiune a bazelor de date distribuite (SGBDD) trebuie să funcționeze la fel, indiferent de locația pe care o accesează, ca și cum datele ar proveni de la o singură sursă. Software-ul care gestionează o bază de date se numește DDBMS (Distributed Database Management System).

Noțiunile bază de date distribuită și bază de date decentralizată reprezintă lucruri diferite. Cea din urmă nu se referă la trimiterea datelor prin intermediul unui sistem de comunicații. Prima implică o colecție de locații interconectate prin intermediul unei rețele, unde fiecare locație are propria bază de date, dar locațiile lucrează împreună ca și cum informația ar fi fost stocată într-un singur loc.

Baza de date distribuită omogen

Un sistem de baze de date distribuite omogen reprezintă o rețea de două sau mai multe baze de date care sunt stocate pe una sau mai multe stații de lucru.

Figură 8 – Ilustrarea unei baze de date distribuite omogen. Figura indică trei baze de date și clienții asociați: (HQ.ACME.COM), Sales (SALES.ACME.COM) și Manufacturing (MFG.ACME.COM)

Figura 8 ilustrează un sistem distribuit, care se conectează la trei baze de date: hq, mfg și sales. O aplicație poate accesa sau modifica simultan datele din diferite baze de date într-un singur mediu distribuit. De exemplu, o singură interogare de la un client „Manufacturing” asupra unei baze locale mfg, poate returna o reuniune de date (joined data) din tabela products a bazei locale mfg și tabela dept a bazei remote hq.

Pentru o aplicație client, locația și platforma bazelor de date sunt transparente. Se pot crea și sinonime pentru obiectele remote într-un sistem distribuit, astfel încât utilizatorul să le poată accesa cu aceeași sintaxă ca a obiectelor locale. De exemplu, dacă suntem conectați la baza de date mfg, dar dorim sa accesăm date din baza de date hq, creând un sinonim asupra tabelului mfg pentru tabelul remote dept, vom putea interoga datele folosind următoarea sintaxă:

SELECT * FROM dept;

În acest fel, un sistem distribuit dă senzația utilizării unei baze de date locale. Utilizatorii din mfg nu au nevoie să știe faptul că datele pe care le accesează se găsesc în bazele de date remote.

Baza de date distribuită heterogen

Un sistem de baze de date este considerat a fi distribuit daca furnizează acces la date ce se găsesc în multiple locații ale rețelei, chiar dacă nu pune la dispoziție facilități depline pentru integrarea schematică („schema integration”), managementul distribuit al interogărilor („distributed query management”) și/sau managementul distribuit al tranzacțiilor („distributed transaction management”). Acest sistem se consideră a fi heterogen dacă nodurile locale conțin tipuri diferite de computere sau de sisteme de operare, chiar dacă toate bazele de date locale sunt bazate pe același model de date și probabil, chiar același SGBD (sistem de gestiune al bazelor de date).

Sistemele distribuite heterogen pun la dispoziție diferite tipuri de capabilități. Printre acestea se numără cele trei de mai sus, împreună cu funcții administrative și copierea cu diferite tipuri de heterogenitate.

Integrarea schematică se referă la modul în care utilizatorul poate vizualiza logic datele distribuite;

Managementul distribuit al interogărilor se ocupă cu analiza, optimizarea și execuția interogărilor care se referă la date distribuite;

Managementul distribuit al tranzacțiilor se referă cu atomicitatea, izolarea și durabilitatea tranzacțiilor într-un sistem distribuit;

Funcțiile administrative includ concepte precum autentificarea, autorizarea, definirea și împuternicirea constrângerilor semantice ale datelor precum și managementul dicționarelor de date și a directoarelor.

Heterogenitatea poate include diferențe la nivelul hardware-ului, sistemelor de operare, căilor de comunicație, precum și la nivelul vânzătorilor sistemelor de gestiune a bazelor de date (DBMS – „Database management Systems”) și/sau a modelelor de date.

Acestea fiind spuse putem considera că am acoperit într-un mod sumar cele mai importante aspecte ale unui sistem distribuit de management al datelor. Este important să recunoaștem că nu există un set „ideal” de capabilități pentru toate mediile sau aplicațiile. O calitate particulară poate fi indispensabilă într-o anumită situație, în timp ce, pentru altele, poate fi complet nepotrivită.

Figură 9 – Ilustrarea unei baze de date distribuite heterogen.

1.4 Securitatea și protecția datelor în bazele de date

Odata cu dezvoltarea tehnologiei informației, organizațiile au acumulat un volum imens de date referitoare la numeroase aspecte din activitățile pe care le desfășoară. Datele pot reprezenta bazele unor decizii critice, ceea ce înseamnă că acestea s-au transformat în resurse extrem de prețioase pentru organizații, deci, este necesară o mai mare atenție referitoare la securitatea acestora. Din aceste cauze, tote persoanele unei organizații trebuie să participe la identificarea pericolelor și să ia măsuri de precauție pentru a proteja informațiile din domeniile în care își desfășoară munca.

În cele ce urmează, vom discuta despre nevoia de securitate într-o bază de date, despre conceptele din spatele accesului și controlului securității într-un sistem de gestiune al bazelor de date, dar și despre diferitele aspecte referitoare la politicile de securitate și procedurile aferente.

1.4.1. Securitatea bazelor de date: o imagine de ansamblu

De cele mai multe ori, problemele legate de securitate sunt complexe și pot implica aspecte legale, sociale sau etice. De asemenea, pot exista și probleme legate de politicile implementate sau legate de controlul echipamentelor fizice.

Securitatea unei baze de date se referă la protejarea acesteia împotriva amenințărilor, fie ele intenționate sau neintenționate, folosind elemente de control care pot să fie, sau să nu fie bazate pe echipamentele hardware.

Analiza securității unei baze de date nu include numai serviciile puse la dispozitie de sistemul de gestiune (DBMS), ci o gamă mai largă de probleme asociate unei baze de date și a mediului în care aceasta este stocată. Mai mult, considerentele de securitate nu se aplică doar datelor pe care baza de date le conține, deoarece lacunele în securitate pot afecta alte ramuri ale sistemul, care la rândul lor pot afecta baza de date.

În consecință, prin concentrarea asupra securității bazei de date nu se poate poate asigura o siguranță totală a acesteia. Toate părțile componente ce alcătuiesc sistemul trebuie să fie securizate: baza de date, rețeaua, sistemul de operare, clădirea în care baza de date se află stocată fizic și persoanele care au oportunitatea să accesese sistemul.

Proiectarea și implementarea unei baze de date sigure implică atingerea următoarelor obiective:

Izolarea (Privacy) – se referă la faptul că datele ar trebui să nu fie accesibile utilizatorilor neautorizați;

Integritatea (Integrity) – se referă la faptul că numai utilizatorii autorizați pot aduce modificări datelor;

Disponibilitatea (Availability) – se referă la faptul că utilizatorilor autorizați nu ar trebui să li se restricționeze accesul la date

.

Pentru a atinge toate aceste obiective, trebuie dezvoltată o politică clară referitoare la securitate ce descrie în mod clar măsurile impuse. În particular, trebuie determinat care dintre utilizatori poate accesa baza de date, precum și datele pe care le pot accesa. Mai mult decât atât, trebuie să specificăm ce operații vor putea efectua asupra datelor.

Apoi, vom apela mecanismul de securitate pus la dispoziție de către DBMS sau de către sistemul de operare. Orice mecanism extern, precum accesul securizat în clăride sau în camera unde se află stocată fizic baza de date, depășește scopul discuției curente.

Figură 10 – Securitatea bazei de date: o imagine de ansamblu

Persoana responsabilă cu securitatea bazei de date este administratorul de baze de date (DBA – Database Administrator), care trebuie să ia în considerare numeroasele posibile amenințări asupra sistemului. Administratorul bazei de date este cel care definește regulile ce determină cine poate să acceseze baza de date, care părți ale acesteia pot fi accesate de fiecare utilizator și ce operații sunt permise.

1.4.2. Necesitatea securității unei baze de date

Motivul pentru care securitatea bazelor de date a devenit o problemă atât de importantă, este datorat creșterii cantității de informație extrem de importantă ce se colectează și se stochează pe calculatoare.

Este de la sine înțeles faptul că, orice pierdere a disponibilității sau a datelor, poate fi dezastruoasă. O bază de date este o colecție de resurse esențiale care trebuie să fie protejată adecvat, folosind elemente specifice.

Amenințările la care sunt supuse datele pot fi situații sau evenimente, intenționate sau neintenționate, ce vor afecta într-un mod negativ sistemul și ulterior și organizația. Daunele cauzate pot fi măsurabile (pierdere de informații) sau nemăsurabile (pierderea credibilității sau a încrederii clienților). Orice amenințare trebuie văzută ca o potențială breșă în securitatea sistemului, care, dacă este exploatată, poate avea un impact imens asupra organizației.

Vom prezenta câteva exemple de posibile amenințări:

Folosirea datelor de către un utilizator ce dispune de un dispozitiv extern de stocare a datelor;

Colectarea sau copierea neautorizată a datelor;

Alterarea programelor;

Acces ilegal al unui hacker;

Furt de informații, programe sau echipamente;

Instruirea inadecvată a personalului;

Dezvăluiri neautorizate de date;

Calamități (incendii, inundații, bombardări);

Stricarea sau deconectarea cablurilor;

Viruși.

Cât de mult suferă organizația, ca rezultat al amenințărilor ce s-au materializat, depinde de numeroși factori, precum existența măsurilor de securitate și a planurilor pentru cazuri excepționale. Cu alte cuvinte, capacitatea unei organizații de a minimiza cantitatea de pagube este cu atât mai mare cu cât aceasta s-a pregătit pentru situații de acest gen.

De exemplu, dacă un dispozitiv hardware cedează și alterează capacitatea stocării secundare (backup), toate activitățile trebuiesc încetate până la rezolvarea problemei. Durata inactivității și rapidatea cu care se restabilește baza de date va depinde de:

Abilitatea personalului de a folosi elemente alternative de hardware si software; în momentul în care ultimul backup a fost executat;

Data la care a fost executat ultimul backup;

Timpul necesar de refacere al sistemului;

Posibilitatea ca datele pierdute să nu mai poată fi recuperate.

Consumarea unei cantități considerabilie de timp, efort și bani pentru rezolvarea unor posibile amenințări care ar avea ca efect inconveniențe minore, se poate dovedi ineficientă din punct de vedere economic. Afacerea poate influența, în schimb, tipurile de amenințări ce se iau in considerare, acestea având o probabilitate mică de a avea loc. Totuși, evenimentele rare ar trebui luate în considerare în cazul în care impacul lor este unul semnificativ.

Indiferent cât de sigur pare un sistem, o securitate adecvată poate fi atinsă numai dacă mediul în care se desfășoară activitatea este și el, la rândul lui, unul sigur.

Următoarele amenințări trebuie să se regăsească în orice plan de securitate complet:

Furt și fraudă. Aceste acțiuni pot fi comise de oameni și pot afecta datele. În acest caz, atenția trebuie îndreptată către toate locațiile unde datele sau aplicațiile sunt stocate fizic. Trebuie stabilită o securitate fizică concretă astfel încât persoanele neautozitate să nu poată avea acces în încăperile unde calculatoarele sau serverele ce conțin datele sunt localizate.

Pierdera izolării sau a confidențialității. Confidențialitatea se referă la nevoia de a păstra datele secrete. Aceasta are o importanță majoră pentru organizație, iar izolarea trebuie să protejeze datele de indivizi. Pierderea izolării poate conduce la pierderea competitivității, iar eșecul în a păstra izolarea poate conduce la șantaj, mituire, umilirea publică sau furtul parolelor utilizatorilor. Unele dintre acestea pot conduce la sancțiuni legale asupra organizației.

Pierderea integrității datelor. Daca integritatea datelor este afectată, datele vor fi greșite, nefolositoare și corupte. În acest caz, organizația poate suferi pierderi importante sau poate lua decizii greșite bazate pe date incorecte.

Pierderea disponibilității. Asta înseamnă că datele, sistemul, sau ambele, nu pot fi accesate. Uneori acest fenomen este urmat de alterarea datelor și poate conduce la dificultăți operaționale severe. Acestă pierdere poate apărea ca rezultat al sabotajului asupra componentelor hardware, asupra rețelei sau a aplicației, sau ca rezultat al introducerii virușilor.

Pierderea accidentală de informații. Acest fenomen poate fi rezultatul unei erori umane, software, sau o breșă în configurația componentelor hardware. Pentru a evita pierderea accidentală a informațiilor, o organizație trebuie să stabilească proceduri clare pentru autorizarea utilizatorilor, instalarea software-ului trebuie să fie uniformă iar componentele hardware trebuiesc întreținute. Dar cum orice acțiune ce implică ființe umane, implică automat și erori inevitabile, trebuie totuși încercată o folosire a politicilor și a procedurilor care asigură pierderile minime.

Securitatea bazelor de date are scopul de a minimiza pierderile cauzate de evenimentele numite mai sus într-un mod eficient în termeni financiari și fără a avea repercursiuni asupra utilizatorilor. De când numărul crimelor bazate pe informatică a explodat și deoarece acest tip de amenințare poate afecta toate componentele unui sistem, înființarea măsurilor adecvate de securitate a devenit vitală.

Capitolul 2. Sisteme de gestiune a bazelor de date (SGBD)

Componentele unui sistem de baze de date

Un sistem de gestiune a bazelor de date (SGBD) are în compoziție patru componente. Fiecare dintre acestea are un rol foarte important în contextul unui asemenea sistem. Ne vom concentra în acest capitol asupra componentelor esențiale din punctul de vedere al aplicației ce urmează a fi descrisă în capitolele următoare.

Așadar, cele patru componente sunt următoarele:

Componenta hardware

Componenta software

Utilizatorii

Date persistente

În cele ce urmează, vom trata pe rând fiecare dintre aceste componente, pentru a oferi o imagine de ansamblu asupra rolului esențial pe care îl joacă un sistem de gestiune a bazelor de date în economia unui proiect real.

2.1.1 Componenta software

Principala componentă a unui SGBD o reprezintă componenta software. Aceasta reprezintă setul de programe folosit pentru a manipula baza de date și pentru a controla și a manageria întreaga bază de date computerizată.

Sistemul software este la rândul său format din următoarele elemente:

Sistemul de gestiune a bazelor de date în sine (componenta software) este cea mai importanta componentă din întregul sistem.

Sistemul de operare împreuna cu software-ul de rețea, acestea fiind folosite pentru a distribui și a oferi accesul la baza de date mai multor utilizatori.

Programele și aplicațiile dezvoltate cu ajutorul limbajelor de programare precum C++, C#, Visual Basic, ce sunt folosite pentru a accesa bazele de date. Fiecare program conține „enunțuri/fraze” („statements”) care interoghează SGBD-ul cu scopul de a efectua operații asupra bazei de date. Operațiile pot fi de select, update, delete și altele, însă acestea sunt cele esențiale. Aceasă aplicație poate funcționa fie online – fiecare utilizator având o copie a acesteia, fie local (pe câte o stație de lucru, baza de date în sine fiind și ea locală – adesea folosită de programatori în scopul dezvoltării și testării aplicațiilor).

Componenta software reprezintă „front-endul” întregului sistem, întrucât aceasta reprezintă puntea/legătura utilizatorului cu baza de date. Aceasta are rol de mediator, însă poate avea și alte roluri. În primul rând, trebuie înteles faptul că această aplicație este creată după modelul bazei de date. Niciodată interfața sau designul aplicației nu va influența în niciun fel structura bazei de date.

Interfața poate avea importantul rol de validator al informației.

Exemplu: Ce reprezintă această validare mai exact? Putem lua ca exemplu un câmp de introducere a CNP-ului unei persoane. Înainte ca prin intermediul interfeței să se realizeze o adăugare sau o modificare a unui CNP în baza de date, se va apela o funcție de validare (funcție ce există în aplicație) pentru a garanta acuratețea și corectitudinea informațiilor introduse. Astfel, la introducerea unui CNP incorect, aplicația va avertiza utilizatorul, prin intermediul unui mesaj sugestiv, că a întrodus o valoare incorectă în câmpul respectiv.

2.1.2. Componenta hadrware

Componenta hardware reprezintă un set de dispozitive electronice fizice, precum calculatoare (împreună cu dispozitivele de Intrare/Ieșire – I/O asociate, precum driverele disk), dispozitive de stocare, canale de intrare/ieșire, dispozitive electromecanice care realizează interfața dintre calculatoare și sistemele din lumea reală și așa mai departe. În interiorul unei rețele, un calcualtor cu o putere mare de procesare și o capacitate mare de stocare, este necesar pe postul de server.

Serverul este locul unde baza de date se află stocată fizic. Astfel, putem remarca faptul că există o strânsă legătură între componenta hardware și performanța bazei de date: cu cât este mai performant serverul, cu atât procesele ce presupun o cantitate mare de procesare se vor încheia mai rapid. În același timp, trebuie avut în vedere faptul că nu toate aplicațiile necesită o bază de date cu o putere mare de procesare, astfel, investirea într-un server perfomant poate fi o risipă de bani.

Componentele hardware reprezintă o mare vulnerabilitate din punct de vedere al securității, după cum s-a discutat și în capitolul precedent – secțiunea referitoare la securitate. Trebuie acordată o deosibită atenție locației unde serverele sunt stocate fizic. O altă caracteristică importantă, ce determină funcționarea în parametrii optimi a serverului, este temperatura. Camera în care sunt depozitate aceste servere trebuie să dispună de un sistem de răcire performant, întrucât procesoarele acestor calculatoare performante se pot încălzi foarte repede în momentul în care sunt folosite intensiv.

2.1.3. Utilizatorii

Există patru tipuri de utilizatori ce au acces la sistemul de gestiune al bazelor de date:

Dezvoltatorii aplicației

Utilizatorii finali

Administratorul bazei de date (DBA)

Analistul sistemului

În cele ce urmează vom trata separat fiecare dintre aceste catorii de utilizatori, fiecare având un rol important în economia SGBD-ului. În timp ce unii se ocupă de partea de programare/dezvoltare, alții se ocupă de partea de întreținere a echipamentelor ce sunt folosite în organizație, cât și de întreținerea serverului în sine (DBA).

Utilizatorii principali sunt cei pentru care s-a construit aplicația și sunt cei care o vor folosi cel mai intens, loc unde de cele mai multe ori apar problemele și erorile datorate greșelilor de programare, suprasolicitarea serverului sau chiar probleme datorate utilizării greșite a aplicației.

2.1.3.1. Dezvoltatorii aplicației

Sunt reprezentați de creatorii aplicației în sine, de la proiectare și planificare până la implementare și testare. Aceștia scriu programe ce interacționează cu baza de date, programe ce pot fi scrise în limbaje de programare precum COBOL, C++, Java sau alte limbaje de programare din a patra generație. Aceste programe accesează baza de date prin intermediul unei interogări adecvate, de cele mai multe ori acestea fiind reprezentate de fraze SQL (Structured Query Language – Limbaj Structurat de Interogare).

Aplicația se află în strânsă legătură cu structura și logica bazei de date, astfel încât arhitecții si designerii bazelor de date ar trebui să lucreze într-un mediu foarte apropiat cu acești dezvoltatori ai aplicației (de multe ori se încurajează amplasarea acestor indivizi în același birou/departament).

De asemenea, acești dezvoltatori se ocupă și de testarea aplicației, întrucât ei sunt cei care au creat-o și îi pot anticipa defectele și lacunele. Orice organizație serioasă pune mare accent pe testarea unei aplicații înaintea distribuirii acesteia către utilizatorii finali, astfel minimizându-se numărul de erori care pot apărea în viitor.

2.1.3.2. Utilizatorii finali

Aceștia sunt clienții aplicației, cei ce o vor folosi cel mai frecvent si cel mai intens. Aceștia trebuie să cunoască modul de funcționare al aplicației, design-ul bazei de date, mecanismele de acces și așa mai departe. Utilizatorii finali se folosesc de aplicației pentru a-și îndeplini sarcinile de lucru.

Această categorie se desparte la rândul ei în două tipuri de utilizatori:

Utilizatorii direcți

Utilizatorii indirecți

Utilizatorii direcți sunt cei care interacționează cu aplicația, cu calculatorul, cu baza de date în mod direct, urmărind instrucțiunile puse la dispoziție de interfața cu utilizatorul. Ei interacționează cu aplicația finală, deja creată, pentru a obține rezultatele dorite. Spre exemplu persoanele de la ghișeul de bilete al căilor ferate interacționează în mod direct cu baza de date.

Utilizatorii indirecți sunt cei care beneficiază de pe urma sistemului în mod indirect. Ei folosesc rezultatele generate de programe pentru a lua decizii sau pentru alte scopuri. Acești utilizatori sunt interesați doar de rezultatele pe care programul le produce și nu de logica după care funcționează aplicația.

2.1.3.3. Administratorul bazei de date (DBA)

Este persoana care ia toate deciziile strategice privind datele întreprinderii, în același timp fiind și cel care pune la dispoziție suportul tehnic necesar luării acestor decizii. Așadar, DBA-ul este responsabil cu controlul total al sistemului din punct de vedere tehnic. În contextul unei baze de date, principala resursă este reprezentată de însăși baza de date, iar a doua este reprezentată de sistemul de gestiune al acesteia (SGBD-ul) împreună cu celelalte componente software asociate. Software-ul ce pune la dispoziție administrarea și managementul resurselor bazei de date cade în grija DBA-ului.

2.1.3.4. Analistul sistemului

Acesta determină cerințele ce li se impun utilizatorilor finali, în același timp fiind și cel care face studii referitoare la aspectele cele mai importante din contextul acestui sistem, cum ar fi fezabilitatea, aspectul economic, tehnic și așa mai departe. Analistul este cel care trebuie să își creeze o imagine de ansamblu, punând accent pe aspectele esențiale din punctul de vedere al organizației, întrucât o aplicație nu se rezumă doar la programare și administrare, ci trebuie să aibă un temei solid, un scop precis, un cost bine calculat și bine investit. De asemenea, în contextul unei aplicații la care se depune o cantitate mare de muncă și implicit generează costuri mari, trebuie luată în considerare și capacitatea acesteia de a genera suficiente informații încât să acopere aceste costuri.

Studiul fezabilității este cel care se ocupă de partea practică, de capacitatea proiectului de a se realiza dar în acelați timp ține cont și de rentabilitatea acestuia. Un proiect considerat nefezabil nu se poate da spre implementare.

2.1.4. Date persistente

Calculatoarele și dispozitivele de stocare sunt pline de date, motiv pentru care există diferite tipuri de date, deosebirea făcându-se din punctul de vedere al frecvenței cu care este accesată sau modificată o anumită informație. Datele persistente sunt acele date care în mod obișnuit nu sunt accesate și rareori sunt modificate. Datele persistente din bazele de date sunt stocate pe un server și sunt accesate mai des decât datele arhivate. Cu ajutorul datelor arhivate sau stocate pe discuri/casete, informația este foarte rar folosită. Arhivarea datelor le permite cercetătorilor să consulte aceste informații vechi pentru a găsi trenduri din trecut care s-ar putea aplica în situațiile prezente.

Datele persistente sunt foarte rar modificate; asta înseamnă că informația este stocată într-o bază de date, discurile și casetele nu sunt schimbate, exceptând cazurile speciale. Informația poate fi accesată destul de rar, fiind modificată și mai rar. Aceste date există pe parcursul mai multor sesiuni, spre deosebire de tipurile de date care există doar pe parcursul unei singure sesiuni, urmând să fie în cele din urmă șterse sau legate de acea singură sesiune.

Cu ajutorul datelor persistente, o întreagă bază de date sau o secțiune a acesteia poate fi creată pentru a stoca datele arhivate. Acest lucru se poate realiza local, pe o bază de date stocată pe unitatea hard a calculatorului, sau poate fi plasată pe un server. Informațiile persistente sunt de cele mai multe ori accesate, spre deosebire de casete sau de discuri, deoarece informatia este imediat accesibilă. În același timp, această bază de date va exista și va fi nemodificată pentru perioade lungi de timp, luni sau chiar ani.

Datele persistente înregistrate pe bandă (casete) sau pe discuri au scopul de a nu fi atinse, nici măcar a fi încărcate într-un calculator pentru ani de zile. Aceste date au scop de arhivă, putând să nu fie accesate niciodată. Informațiile persistente sunt de obicei de dimensiuni mari, deci sunt șterse de pe hardurile locale odată ce au fost arhivate; așadar casetele sau discurile reprezintă de cele mai multe ori singura copie a datelor existente.

Există numeroase motive pentru care dezvoltatorii sau administratorii preferă să arhiveze datele persistente. În timp ce datele nu reprezintă interes pe moment, acestea trebuie să rămână disponibile pentru cercetări viitoare.

2.2 Limbaje SGBD

Ușurința cu care se pot obține informațiile de la o bază de date determină factorul de decizie cel mai important în alegerea unui limbaj SGBD. În contrast cu sistemele mai vechi, sistemele de baze de date relaționale permit adresarea cu ușurință a unei clase bogate de întrebări; acestă trăsătură a contribuit foarte mult la popularitatea acestor limbaje. Considerăm contextul aplicației noastre: o companie mare ce are în subordine un număr mare de angajați. Acestea sunt unele dintre întrebările pe care un utilizator și le poate adresa:

Care este numele și prenumele angajatului cu codul 24425?

Care este salariul mediu al unui angajat din departamentul C213?

Câți angajați fac parte din departamentul C213?

Aceste întrebări ce implică date stocate în SGBD poartă numele de interogări (queries). Un sistem de gestiune al bazelor de date pune la dispoziție un limbaj specializat, numit limbaj de interogare, în care aceste întrebări pot fi adresate bazei de date. Un aspect foarte atractiv al modelului relațional îl reprezintă capacitatea de a suporta limbaje de interogare puternice. Calculul relațional este un limbaj de interogare formal bazat pe logică matematică, iar interogările scrise în acest limbaj au un înțeles precis și intuitiv.

Un SGBD pune mare preț pe evaluarea într-un mod cât mai eficient posibil a unei interogări. Desigur, evaluarea eficienței unei interogări este determinată în mare măsură de modul în care datele sunt stocate fizic. Se pot folosi indecși pentru a grăbi interogările – de fapt, o bună strategie de folosire a indecșilor poate mări semnificativ viteza cu care este executată interogarea.

Un sistem de gestiune al bazelor de date îi permite utilizatorului să creeze, să modifice și să interogheze date prin intermediul unui limbaj de manipulare (Data Manipulation Language – DML).

Limbajul de manipulare al datelor reprezintă o familie de elemente sintactice similare limbajelor de programare folosite pentru a selecta, a insera, a șterge și a modifica informații într-o bază de date. Unul dintre cele mai populare limbaje de manipulare al datelor este limbajul SQL – Structured Query Language (Limbaj de interogare structurat), care este folosit pentru a prelua și a manipula date într-o bază de date relațională. Există și alte limbaje DML folosite de bazele de date CODASYL, precum IDMS și altele. În contextul aplicației noastre, ne vom axa pe limbajul SQL.

Limbajul de manipulare a datelor cuprinde frazele cu scop de alterare a datelor SQL (data change statements), care modifică datele stocate însă nu alterează și schema sau obiectele bazei de date. Manipularea obiectelor bazei de date (de exemplu tabelele, procedurile stocate etc.) prin intermediul frazelor SQL ține de limbajul DDL (Data Definition Language). În SQL, aceste două categori sunt similare în sintaxă, tipuri de date, expresii etc, dar diferă prin funcționalitatea lor generală.

Aceste limbaje sunt structurate după acțiunea pe care o dezvoltă asupra datelor prin intermediul primului cuvânt din frază, care aproape întotdeauna este un verb. În cazul limbajului SQL, aceste verbe sunt:

SELECT … FROM … WHERE …

INSERT INTO … VALUES …

UPDATE … SET … WHERE …

DELETE FROM … WHERE …

Interogarea SELECT reprezintă o operație pur „read-only” (doar citire), motiv pentru care a fost exclusă din DML, deoarece nu modifică datele în nici un fel, iar funcțiile din cadrul DML-ului au scopul de manipulare a datelor. În practică însă nu se mai realizează această distincție și de multe ori instrucțiunea SELECT este considerată parte componentă a DML-ului.

Majoritatea implementărilor limbajului SQL extind capabilitățile acestuia punând la dispoziție limbaje procedurale, precum PL/SQL pentru bazele de date Oracle. Limbajele de manipulare a datelor prezintă multiple diferențe între ele, datorate producătorului bazelor de date.

Aceste limbaje sunt divizate în două categori:

Programare procedurală

Programare declarativă

Programarea procedurală este derivată din programarea structurată și se bazează pe conceptul apelării procedurilor. Procedurile – numite și rutine, subrutine, metode sau funcții (a nu se confunda cu funcțiile matematice, dar similare cu cele folosite in programarea funcțională) – conțin în esență serii de pași de calcul. O procedură poate fi apelată în orice moment, pe întreaga durată a execuției programului. Exemple de limbaje de programare procedurală: C, Fortran, Pascal și BASIC (Beginner's All-purpose Symbolic Instruction Code).

Programarea declarativă se bazează pe crearea unei structuri și a unor elemente de programare, care exprimă logica unui calcul, fără a-i descrie fluxul/modul de funcționare. Multe limbaje aplică acest stil în încercarea de a minimiza și chiar de a elimina efectele secundare ale programării, descriind ceea ce programul ar trebui să realizeze (scopul final), în loc de a descrie explicit modul în care acesta va funcționa (programarea în sine).

Vom prezenta un exemplu de comandă INSERT într-o tabelă cu angajați:

INSERT INTO Angajati(nume, prenume) VALUES ('Dumitrache', 'Valentin')

2.3. Interfețe SGBD

În funcție de tipul de utilizator al sistemului de gestiune al bazelor de date, dar și în funcție de nevoile acestuia, există numeroase interfețe diferențiate prin aspect, dar și prin funcțiile pe care le pune la dispoziție acestuia.

În cele ce urmează, vom discuta pe rând despre fiecare dintre următoarele tipuri de interfețe:

Interfețe bazate pe meniuri

Interfețe bazate pe forme

Interfețe grafice

Interfețe în limbaj natural

Interfețe specializate aferente cererilor repetate

Interfețe pentru administratorii bazelor de date (DBA)

2.3.1. Interfețe bazate pe meniuri

Aceste interfețe pun la dispoziție utilizatorului un set de opțiuni, numite meniuri, menite să îl conducă pe acesta prin toți pașii necesari formulării unei cereri. Aceste meniuri înglobează toate comenzile specifice bazei de date, reținând sintaxa corecta de formulare a cererii, cerere ce se generează de către utilizator prin selecția unor opțiuni listate de către interfață. Meniurile de tip „pull-down” (tragere-în-jos) sunt foarte populare în rândul interfețelor web, însă se regăsesc și în aplicațiile de căutare, foarte folositoare utilizatorilor deoarece îi ghidează pas cu pas, având acces la conținutul bazei de date, datele prezentându-i-se într-o manieră nestructurată (mai simplă și de cele mai multe ori preferată de către utilizatorii obișnuiți).

Interfețele bazate pe meniuri reprezintă o alternativă sigură și foarte facilă, întrucât utilizatorii sunt ajutați și îndrumați de către structura și ordinea în care aceste meniuri devin accesibile pe parcursul construcției interogării. Dinamicitatea și sugestivitatea acestui tip de interfețe s-au dovedit extrem de eficiente, întrucât permiteau accesul unei noi clase de utilizatori – cei mai puțin experimentați – lucru posibil datorită caracterului interactiv al acestora. Simpla idee care a stat la baza acestui mecanism – alegerea unei opțiuni dintr-o listă predefinită – a fost una atât de inspirată încât era pur și simplu imposibilă apariția erorilor datorate unei utilizări incorecte a interfeței. Datorită acestui caracter interactiv, utilizatorul era ghidat pe drumul cel bun, nelăsându-i-se loc de greșeli (fie ele și intenționate).

Siguranța și ușurința cu care s-au folosit aceste interfețe de-a lungul anilor le-au facut să reziste până în zilele noastre și vor continua să existe atâta timp cât mecanismul de interogare al unei baze de date se poate reprezenta treptat.

Exemplu: Un utilizator de la o firmă (tipul firmei este irelevant) dorește să întocmească o situație a angajaților dintr-un anumit departament. Tot ceea ce trebuia să facă utilizatorul era să aleagă succesiv una dintre opțiunile puse la dispoziție de către interfață.

Întrucât dorește să întocmească o situație (ceea ce implică doar vizualizarea datelor), înseamnă că urmează să aleagă comanda SELECT din lista tuturor comenzilor posibile (INSERT, UPDATE, DELETE);

Își aleage tabela dorită (în cazul nostru tabela ce conține informațiile referitoare la angajați);

Își alege câmpurile care îl interesează (nume, prenume, sau pur și simplu numărul tuturor angajaților);

Își alege modul de filtrare al datelor (în cazul nostru, după departament).

Vizualizază datele.

Se poate oberva ușurința cu care se construiește această interogare, într-un mod treptat și sugestiv. De asemenea, un alt avantaj îl reprezintă faptul că utilizatorul nu trebuie să aibe cunoștințe legate de structura bazei de date.

2.3.2. Interfețe bazate pe forme

O interfață grafică bazată pe forme (forms) prezintă câte un formular fiecărui utilizator. Acesta poate completa toate câmpurile formularului pentru a insera date noi, sau poate completa doar câteva câmpuri, caz în care SGBD-ul va prelua date potrivite pentru celelalte câmpuri.

Formularele sunt în mod normal gândite și programate pentru utilizatorii naivi deoarece interfața conservă tranzacțile. Multe sisteme de gestiune ale bazelor de date au limbaje specifice pentru crearea acestor formulare, acestea fiind limbaje speciale care ajută programatorii în crearea lor. Unele sisteme permit definirea unor noi formulare lăsând utilizatorul să le construiască într-un mod interactiv cu ajutorul interfeței.

2.3.3. Interfețe grafice

O interfață grafică cu utilizatorul (GUI – Graphical User Interface) are rolul de a prezenta o schemă sub o formă dinamică. Acesta poate crea o interogare prin manipularea diagramei. În majoritatea cazurilor, GUI-ul utilizează atât meniuri cât și formulare. Cele mai multe GUI-ui folosesc dispozitive de indicare, precum un mouse, pentru a alege anumite părți ale diagramei vizibile pe ecran.

Aceste interfețe sunt ceva mai avansate, întrucât au apărut mai târziu și au împrumutat idei de la predecesoarele lor, ajungându-se astfel la obținerea unei interfețe atrăgătoare atât din punct de vedere vizual, cât și din punctul de vedere al ușurinței cu care se folosește.

2.3.4. Interfețe în limbaj natural

Aceste interfețe acceptă cereri scrise în limba engleză sau altă limbă și încearcă să le „înțeleagă”. O interfață în limbaj natural are în mod normal propria ei „schemă”, similară cu schema conceptuală a bazei de date, dar și un dicționar de cuvinte cheie.

Interfața în limbaj natural se referă la cuvintele din schema ei proprie, pentru a interpreta cererea. Dacă interpretarea se realizează cu succes, interfața generează o interogare de nivel înalt, corespunzătoare cererii realizate în limbaj natural și o adresează SGBD-ului pentru procesare; în caz contrar, se inițiază un dialog cu utilizatorul pentru a se clarifica interogarea.

Limbajele de nivel înalt din ziua de astăzi permit formularea interogărilor într-un limbaj foarte apropriat de cel natural, întrucât cuvintele cheie ale limbajelor sunt foarte apropiate de exprimarea umană.

2.3.5. Interfețe specializate aferente cererilor repetate

Operațiile repetate sunt de cele mai multe ori folosite de către utilizatorii care au un set mic de operații, pe care le execută folosind anumiți parametrii. Analiștii sistemului și programatorii crează și programează o interfață speciala pentru fiecare clasă/tip de utilizator. De obicei, un set mic de comenzi abreviate este adăugat, cu scopul de a minimiza numărul de tastări (apăsări ale tastaturii) necesar execuției oricărei interogări.

Aceste interfețe au rolul de a garanta accesul rapid utilizatorilor, dat fiind faptul că este folosit în locuri unde pierderea de timp reprezintă și o pierdere de bani. De exemplu în bănci, un funcționar trebuie să fie capabil să proceseze câți mai mulți clienți într-un timp cât mai scurt. De asemenea aceste sisteme sunt foarte sigure, datorită numărului mic de operații pe care îl exercită asupra bazei de date. Aceste cereri repetate sunt verificate și răsverificate de fiecare programator și în final de fiecare funcționar.

2.3.6. Interfețe pentru administratorii bazelor de date (DBA)

Majoritatea bazelor de date conțin comenzi specifice administratorilor bazelor de date. Printre acestea se numără comenzi pentru crearea conturilor, pentru a le configura drepturile (conturile pot avea drepturi diferite în funcție de rolul pe care îl are utilizatorul respectiv), pentru schimbarea parametrilor bazei de date (memoria pe care o ocupă, procesoarele pe care le folosește etc.), pentru schimbarea schemei și multe altele.

Aceste interfețe nu sunt accesibile utilizatorilor obișnuiți întrucât acestea sunt interfețele cu care se pot produce cele mai mari pagube (în cazul în care sunt folosite necorespunzător). Administratorul bazei de date este utilizatorul cel mai important și cu drepturile cele mai mari asupra unei baze de date. El gestionează tot ce ține de la stocarea datelor pe server până la activitatea procesoarelor serverului (activitatea pe server trebuie monitorizată constant pentru a se asigura performanța acestuia).

Acesta se ocupă și cu backup-ul datelor, este responsabil de siguranța și de integritatea acestora, în același timp fiind mereu disponibil să rezolve probleme ce survin în utilizarea de zi cu zi a bazei de date. Problemele pot fi multiple și din motive de multe ori greu de explicat. Un anumit utilizator își poate pierde complet accesul, unuia îi pot dispărea drepturi, altul poate experimenta dificultăți în executarea interogărilor (rezultat al unui server suprasolicitat). Astfel, DBA-ul trebuie să fie pe cât posibil capabil să rezolve aceste probleme la orice oră, pentru că un sistem care nu funcționează nu poate genera profit.

Se poate spune cu siguranță că acest tip interfață se adresează celui mai important tip de utilizator, având un rol esențial în buna funcționare al unui sistem informatic.

2.4. Exemple de SGBD

La ora actuală există numeroase sisteme de gestiune a bazelor de date, ce prezintă diferențe însemnate între ele, de la interfața grafică pe care o prezintă utilizatorului până la modul de management al datelor. În cele ce urmează vom prezenta câteva dintre cele mai folosite SGBD-uri, urmând ca ulterior să insistăm asupra sistemului folosit în dezvolarea aplicației prezentate în această lucrare.

Fiecare companie deține informații ce trebuiesc stocate și manageriate pentru a putea fi utile. Spre exemplu, în cazul aplicației noastre, o firmă trbeuie să dețină informații legate de angajații săi. Această informație trebuie să fie disponibilă la orice moment diferitelor persoane din cadrul firmei. Un sistem informațional este un sistem format pentru a stoca și a procesa informația.

Un sistem informațional poate fi reprezentat și de o colecție de dosate, împreună cu regulile de stocare și regăsire a acestora. În mod evident această practică s-a dovedit ineficientă și în zilele noastre, în care informatica a progresat mai mult decât orice alt domeniu, ar fi de-a dreptul lipsită de inspirație alegerea acestui tip de sistem informatic. În prezent, toate companiile mari se folosesc de baze de date pentru a-și automa și ușura munca legată de stocarea informațiilor.

O bază de date este o colecție organizată de informații tratate unitar. Scopul unei baze de date este de a colecta, de a stoca și de a pune la dispoziție informațiile necesare aplicațiilor ce folosesc baza de date.

Un sistem de gestiune al bazelor de date (SGBD/DBMS) este un sistem software ce controlează stocarea, organizarea și regăsirea datelor. În mod normal, un DBMS este constituit din următoarele componente:

Codul Kernel – acest cod se ocupă de managementul memoriei și de partea de stocare a unui SGBD.

Depozitul de metadate – este numit adesea și un dicționar de date și conține informații referitoare la structura și proprietățile bazei de date.

Limbaj de interogare (Query language) – este limbajul care le permite aplicațiilor sa acceseze informațiile.

2.4.1. Oracle

Oracle Database (sau simplu Oracle) este un sistem de management al bazelor de date relațional bazat pe obiecte, produs și distribuit de către Oracle Corporation.

Versiunea curentă de Oracle Database este rezultatul a peste 30 de ani de dezvoltare plină de inovații. Vom prezenta sumar un scurt istoric al bazelor de date Oracle (întrucât este unul dintre cele mai populare SGBD-uri din lume).

Evenimente importante în istoria Oracle:

Fondarea companiei – în anul 1977, Larry Ellison, Bob Miner și Ed Oates au înființat consultanța Software Development Laboratories, care în scurt timp a devenit Relational Software, Inc. (RSI). În anul 1983, RSI a devenit Oracle Systems Corporation și mai târziu Oracle Corporation.

Apariția primului RDBMS (Relational Database Management System) comercializat – în anul 1979, RSI a lansat a doua versiune Oracle (V2 – Version 2) drept primul RDBMS bazat pe SQL comercializat, un reper important în istoria bazelor de date relaționale.

Versiunea portabilă a Oracle Database – Versiunea a treia a sistemului Oracle, lansată în anul 1983, a fost prima bayă de date relațională ce putea rula pe calculatoare, minicalculatoare și calculatoare personale (PC-uri). Baza de date era programată in C, permițând-ui acesteia să fie „portată” pe mai multe platforme.

Programele stocate PL/SQL – Oracle7, lansat în anul 1992, a introdus procedurile stocate PL/SQL și triggerele.

2.4.1.1. Versiunile i, g și c

Totul a început în anul 1999 cu versiunea 8i, „i”-ul reflectând suportul pentru mașinile virtuale JVM (Java Virtual Machine) care funcționau pe Internet. Oracle 10g a fost introdus pentru a îmbunătăți lucrul în rețea (grid = „g”), care acum permitea folosirea clusterilor low-cost, iar serverele industriale puteau fi tratate unitar. Versiunea Oracle 12c (2013) era dotată cu multiple servere de sharing pe cloud.

2.4.1.2. Java Built In

Datorită JVM-ului (Java Virtual Machine), acum inclus (built in) în sistemul de gestiune (DBMS), triggerele și procedurile stocate puteau fi scrise și executate în Java în loc de limbajul de programare PL/SQL. Le permite dezvoltatorilor de pe internet să scrie aplicații și proceduri ale bazei de date în același limbaj. În plus, JVM-ul poate de asemeni să execute și Enterprise JavaBeans (EJBs), transformând astfel DBMS-ul întro aplicație server.

2.4.2. Microsoft SQL Server

Este un RDBMS (Relational Database Management System) dezvoltat de Microsoft. Din punct de vedere al serverului bazelor de date, este un produs software a cărui funcție principală este de a stoca și a regăsi cu ajutorul unei cereri ce provine din partea altei aplicații, conexiunea realizându-se local (aplicația se află pe același computer cu baza de date) sau prin rețea, baza de date aflându-se stocată fizic pe un server conectat și el la această rețea.

De-a lungul timpului au existat numeroase versiuni ale acestui program, care aveau diferiți utilizatori țintă, pornind de la aplicații ce rulau pe o singură mașină până la aplicații enorme accesate concurent de numeroși utilizatori. Limbajul principal de programare al acestui RDBMS este T-SQL (Transact SQL) și ANSI SQL. În cele ce urmează vom prezenta un scurt istoric urmând să ne concentrăm apoi pe partea de programare, întrucât aplicația prezentată în acestă lucrare se bazează pe acest produs software.

2.4.2.1. Programarea în Microsoft SQL Server

T-SQL (Transact-SQL) este principalul limbaj folosit la programarea și managerierea SQL Server. Pune la dispozitie cuvinte cheie folosite pentru executarea operațiilor în SQL Server, printre acestea regăsindu-se și cuvinte ce crează și alterează schemele bazei de date, introduc și editează date, precum și cuvinte ce permit monitorizarea și managerierea serverului. Aplicațiile client pot folosi aceste cuvinte cheie pentru a consuma date sau pentru a manageria serverul, trimițând fraze ce conțin cereri către server pentru a fi interpretate și rezultatul lor trimit înapoi aplicației client.

SQL Server permite managerierea sa folosind T-SQL. Acesta expune tabele read-only ce conțin informații referitoare la statisticile serverului și funcționalitatea serverului este pusă la dispoziția utilizatorului T-SQL printr-un sistem-definit de proceduri stocate ce pot fi invocate pentru a efectua operațiile dorite. Se pot crea și servere legate (linked), pentru care T-SQL pune la dispoziție operații specifice multiplelor servere, operații ce pot fi executate prin intermediul unei singure cereri.

Următoarea listă conține o listă de tipuri de aplicații care pot genera cod Transact-SQL:

Aplicațiile de productivitate generale office;

Aplicații ce folosesc o interfață grafică (GUI) ce permite utilizatorului să vizualizeze datele;

Aplicații care folosesc un limbaj general de interogări ce determină datele pe care utilizatorul vrea să le vizualizeze;

Linii de aplicații business ce își stochează datele în baze de date SQL Server. Aceste aplicații pot include atât aplicații scrise de producători și aplicații scrise de către programatorii locali;

Scripturi Transact-SQL ce sunt executate folosind utilitare precum sqlcmd;

Aplicații create folosind sisteme de dezvoltare precum Microsoft Visual C++, Microsoft Visual Basic, Microsoft Visual J++, Microsoft Visual Studio (limbajul în care aplicația noastră este dezvoltată) care folosesc API-uri precum ADO, OLE DB și ODBC;

Pagini web care extrag date din bazele de date SQL Server;

Sisteme de baze de date distribuite în care datele din SQL Server sunt replicate în numeroase baze de date, sau sunt executate interogări distribuite;

Depozite de date în care acestea sunt extrase prin intermediul tranzacțiilor procesate online (OLTP – OnLine Transaction Processing).

Capitolul 3. Proiectarea bazelor de date

Ce este proiectarea?

Proiectarea unei baze de date reprezinta procesul prin care se produce un model detaliat al datelor, necesar construcției oricărei baze de date. Modelul logic al datelor conține toate nevoile logice și fizice ale proiectării, precum și parametrii fizici necesari generării designului într-un limbaj de definire a datelor (data definition language), care ulterior va servi la crearea bazei de date. Un model de date complex și complet conține atribute detaliate specifice fiecărei entități.

Termenul de proiectare este folosit aici la modul general, întrucât acesta poate descrie diferite părți ale unei baze de date.

Procesul de proiectare al unei baze de date constă în general într-un număr de pași ce vor fi executați de către designerul bazei de date. În mod normal, designerul trebuie să:

Determine relațiile dintre diferitele elemente.

Impună o structură logică a datelor pe baza acestor relații.

Modelul entitate-relație. Obiectele bazelor de date relaționale

O teorie importantă în dezvoltarea unui model entitate-relație (ER) implică noțiunea dependențelor funcționale (FD – functional dependency). Precum constrângerile, FD-urile sunt construite pe baza semanticii aplicației. Dependențele funcționale descriu modul în care atributele individuale sunt relaționate. Acestea au rol de constrângere în contextul relațiilor dintre atribute și contribuie la obținerea unei scheme de relaționare corecte.

În general, designul unei baze de date relaționale reușite trebuie să conțină toate atributele și asocierile necesare. Proiectarea ar trebui să îndeplinească toate aceste cerințe cu o cantitate minimă de date stocate și fără redundanțe.q

În designul unei baze de date, redundanța este în general evitată deoarece cauzează probleme din punctul de vedere al menținerii consistenței datelor la updateuri (modificări). Totuși, redundanța poate conduce uneori la îmbunătățiri ale performanței; spre exemplu, când redundanța poate fi folosită în locul unei instrucțiuni join între date. Un join este folosit când utilizatorul are nevoie să obțină informații din două sau mai multe tabele relaționate.

Tabelă (relație)

O tabelă reprezintă o colecție de date relaționate, depozitate structurat și formatat în cadrul unei baze de date. Se compune din câmpuri (coloane) și rânduri.

În bazele de date relaționate, o tabelă reprezintă un set de date (valori) folosint un model vertical de coloane (care sunt identificare prin numele lor) și orizontal pe rânduri, celula fiind unitatea unde un rând se intersectează cu o coloană. Un tabel are un anumit număr de coloane, dar poate avea orice număr de rânduri. Fiecare rând se identifică prin una sau mai multe valori ce apar într-un anumit set de coloane. Setul de coloane ce identifică în mod unic un rând se numește cheie primară.

Tabela mai poartă și numele de relație, deși între acești doi termeni există diferențe. De exmplu, o tabelă conține mai multe rânduri (printre care pot fi și duplicate), în schimb ce relația conține un set de înregistrări ce exclude duplicatele.

Pe lângă informația ce se stochează într-o tabelă, acesta conține și un set de informații numit metadata, informații ce se referă la constrângeri și la structura tabelei. Un alt tip de obiect din baza de date asemănător ca structura cu un tabel este vederea (view), singura deosebire fiind aceea că în cazul vederii, datele sunt calculate și adunate în momentul execuției interogării.

Câmp (atribut)

În domeniul informaticii, datele sunt stocate sub formă de înregistrări (într-o bază de date). Aceste înregistrări sunt formate din mai multe câmpuri. În contextul aplicației noastre, într-o bază de date relațională, mai precis, într-un tabel, informațiile sunt stocate la nivel de înregistrare sau rând. Acest rând este format din mai multe câmpuri sau coloane.

Acest câmp se poate numi și coloană. Câmpul reprezintă locul unde datele sunt efectiv stocate, fiecare coloană având un anumit tip (de exemplu int, datetime, numeric, varchar etc.). La nivel de structură, coloanele sunt unitatea esențială din care se compune structura unei tabele.

Câmpurile mai poartă și denumirea de atribute, datorită faptului că acestea alcătuiesc descrierea unei tabele. De exemplu, o tabelă ce conține informații despre Persoane conține atributele unei persoane, cum ar fi numele, prenumele, cnp-ul, adresa etc. Sunt echivalentele celulelor dintr-un tabel.

Înregistrare (tuplu)

În contextul unei baze de dare relaționale, o înregistrare – denumită și rând – reprezintă o structură simplă, alcătuită din câmpuri sau coloane (3.2.2). Rândurile sunt unitățile componente care stau la baza informațiilor dintr-o tabelă (3.2.1). Fiecare rând dintr-o tabelă reprezintă informații relaționate și fiecare dintre acestea are aceeași structură. Spre exemplu, într-un tabel cu Persoane, fiecare rând ar reprezenta o persoană. În cazul unei asocieri între două tabele (spre exemplu între tabelele Persoane și Departamente, fiecare rând – persoană – ar aparține de un departament). Așadar relaționările se realizează la nivel de înregistrare.

Structura unui rând este determinată de coloanele (3.2.2) acestuia, întrucât fiecare coloană impune și acceptă introducerea un anumit tip de informație în aceasta.

Construcția schemelor relație

Relațiile reprezintă elementul ce ține tabelele împreună (interconectate/legate). Sunt folosite pentru a conecta informațiile relaționate dintre tabele.

Puterea acestor relații se bazează pe modul în care cheia primara a unei entități relaționate este definită. O relație se numește slabă dacă cheia primară a entității relaționate nu conține o componentă de cheie primară a entității părinte.

Folosim drept exemplu o bază de date a unei companii:

Client(IdClient, Nume)

Comandă(IdComandă, IdClient, Dată)

O relație este considerată puternică atunci când cheia primară a entității relaționate conține componenta de cheie primară a entității părinte. Un exemplu este următorul:

Curs(IdCurs, Descriere)

Oră(IdCurs, Secție, Durată, …)

Relația unul al mai mulți (1-M)

O relație de tip unul la mai mulți (1-M) este de regulă folosită în toate bazele de date relaționale și sugerează legătura dintre o entitate principală și entitățile aferente acesteia. Spre exemplu, în contextul aplicației noastre, un departament are mai mulți angajati.

Relația unu la unu (1-1)

O relație de tipul unu la unu este relația dintre două entități (o entitate este relaționată cu o singură altă entitate), și vice versa. Ar trebui să fie un tip de relație destul de rar folosit în designul unei baze de date. De fapt, ar putea sugera faptul că două entități aparțin de fapt aceluiași tabel. Un exemplu de relație de acest tip din aplicația noastre se poate găsi între tabelel AngajatiID și OperatoriID.

Relația M-N

Pentru o relație de tip „many to many”, următoarele afirmații sunt adevărate:

Nu se poate implementa ca atare în modelul de date relațional;

Se poate transforma în două relații de tip 1-M (one to many);

Poate fi implementată „rupând” relația many to many pentru a produce un set de relații 1-M;

Implică implementarea unei înregistrări compuse;

Tabelul ce conține aceste înregistrări compuse trebuie să conțină cel putin cheile primare ale tabelelor originale;

Tabela de legătură conține multiple înregistrări ale cheilor străine;

Atribute adiționale ar trebui adăugate;

Se pot evita probleme datorate relaționării M-N prin crearea înregistrărilor compuse. Spre exemplu, un angajat poate munci la mai multe proiecte sau un proiect poate avea mai mulți angajați ce lucrează la el, în funcție de regulile stabilite. Sau, un student poate participa la mai multe cursuri, iar un curs poate avea mai multi studenți participanți.

Exemplu de mapare a unui tip de relație binară M-N

Pentru fiecare relație binară M-N, identificați două relații.

A și B reprezintă cele două tipuri de entități participante la relțatia R.

Creați o nouă relație S pentru a reprezenta R.

S trebuie să conțină cheile primare ale tabelelor A și B. Acestea împreună pot compune cheia primară în tabelul S sau aceste două împreună cu alte atribute simple din noul tabel pot compune cheia primară.

Combinarea cheilor primare (A și B) vor produce cheia primară a tabelei S.

Relația unitară

O relație unitară, denumită și recursivă, reprezintă relația care are loc între același set de date. În aceast tip de relație, cheile primare și străine sunt aceleași, dar reprezintă două entități diferite cu roluri diferite. Pentru unele entități din relația unitară, o coloană separată poate fi creată pentru a referenția cheia primară a aceluiași set de date.

În figura de mai sus, chiar angajatul este cel ce supervizează un alt angajat, de aceea s-a simțit nevoia introduceri acestui tip de relație.

Forme normale

Normalizarea este o un concept folosit în designul bazelor de date relaționale ce are scopul de a transforma schemele relaționale într-o formă mult mai inteligibilă. Scopul final îl reprezintă eliminarea redundanțelor în cadrul relațiilor și a problemelor aferente, mai exact anomaliile la inserare, ștergere și modificare.

Formele normale au scopul de a ajuta baza de date să progreseze spre obținerea unei structuri optime. Această normalizare este un proces ce se realizează în mai mulți pași, unde fiecare pas aduce câte ceva nou, transformând schema relațională într-o formă „și mai normală”. Fiecare formă normală conține deja formele normale anterioare, în schimb aduce îmbunătățiri din punctul de vedere al optimizării acestora.

Prima formă normală (1NF)

Se poate spune că o relație satisface prima formă normală dacă toate atributele fac parte din domenii care sunt divizate atomic.

Conceptul de divizare atomică a unui atribut asigură faptul că nu există grupuri ce se repetă. Această problemă se datorează sistemului de management al bazei de date relaționale este construit în așa fel încât nu poate reține decât o singură informație la intersecția unui rând cu o coloană. Grupurile repetate se produc atunci când încercăm să stocăm mai multe informații la intersecția unui rând cu o coloană. Drept rezultat, tabelul ce conține astfel de situații nu se consideră a fi strict relațional.

Se poate afirma că un tabel se află în prima formă normală dacă satisface următoarele cinci condiții:

Nu există nici o ordonare de sus în jos a rândurilor.

Nu există nici o ordonare de la stânga la dreapta a coloanelor.

Nu există rânduri duplicate.

Fiecare intersecție a rândului cu coloana conține exact o informație pentru domeniul în care se aplică (și nimic altceva).

Toate coloanele sunt normale (adică nu există componente ascunse)

A doua formă normală (2NF)

O relație se află în a doua formă normală atunci când se află deja în prima formă normală (după cum am discutat anterior – 3.4 – o formă normală o implică pe alta, procesul de normalizare fiind unul treptat) și nu există nici un atribut non-cheie care să depindă de o parte a cheii canditat, ci de întreaga cheie candidat. Se poate deduce faptul că o relație care este formată dintr-un singur atribut crept cheie candidat face parte din 2NF.

A treia formă normală (3NF)

O relație se află în a treia formă normală dacă se află deja în a doua formă normală și nu există nici un atribut non-cheie care să depindă tranzitiv de o cheie candidat. Asta reprezintă faptul că un atribut depinde în mod direct de cheia primară, nu printr-o relație tranzitivă unde un atribut x poate depinde de atributul (care nu face parte din cheie) y și y depinde de cheia primară.

Se poate spune pe scurt că o relație aflată în a treia formă normală este o relație în care toate atributele non-cheie sunt mutual independente.

Forma normală Boyce-Codd (BCNF)

Forma normală Boyce-Codd este o versiune mai strictă a 3NF care se aplică în relațiile în cazul cărora pot exista chei candidate suprapuse.

O relație se aflâ în forma normală Boyce-Codd dacă se află în 3NF și orice dependință funcțională netrivială dată pentru această relație are o cheie candidat drept determinant. Altfel spus, pentru fiecare A B, A este o cheie candidat.

Capitolul 4. Limbajul standard SQL

Introducere în limbajul SQL

Structured Query Language (SQL) reprezintă un limbaj de nivel înalt ce permite manipularea datelor relaționate. Unul dintre punctele forte ale limbajului SQL este acela că utilizatorii trebuie doar să specifice informațiile de care au nevoie fără a trebui să știe modul de preluare al acestor informații. Sistemul de management al bazei de date este responsabil de modul de proiectare și de acces necesar pentru a prelua informațiile.

SQL-ul are trei tipuri de funcționalități:

DDL – Data Definition Language (limbaj de definire a datelor) folosit pentru a defini, a schimba sau a șterge obiectele bazei de date;

DML – Data Manipulation Language (limbaj de manipulare al datelor) folosit pentru a accesa și a modifica datele;

DCL – Data Control Language (limbaj de control al datelor) folosit pentur a da sau a lua drepturi utilizatorilor.

Scurt istoric al limbajului SQL

Totul a pornit din momentul în care compania Sybase SQL Server a vândut codul pe care s-a bazat prima aplicatie MS SQL companiei Microsoft. Acest cod a reprezentat intrarea firmei Microsoft în piața bazelor de date de nivel enterprise (nivel superior). Principalii competitori ai acesteia au fost Oracle, IBM și ulterior, chiar Sybase.

În anul 1989, Microsoft împreună cu Sybase și Ashton-Tate (o companie americană cunoscută pentru dezvoltarea aplicației software dBASE database – unul dintre primele și cele mai de succes SGBD-uri) și-au unit forțele pentru a crea și a comercializa prima versiune numită SQL Server 1.0 pentru OS/2 (o serie de sisteme de operare pentru calculatoare, inițial creată de Microsoft și IBM și mai târziu dezvoltată doar de către IBM).

Versiunea Microsoft SQL Server 4.1 a fost livrată în jurul anului 1992 – împreună cu sistemul de operare OS/2, versiunea 1.3 a celor de la IBM. Mai târziu, versiunea 4.21 se adresa strict sistemului de operare Windows NT (New Technology) și a fost lansat în același timp cu versiunea 3.1 a acestuia.

În dezvoltarea aplicației noastre am folosit ca SGBD versiunea SQL Server 2008 R2, ce a fost lansată la data de 21 Aprilie 2010. Aceasta a adus multe imbunătățiri predecesoarei sale versiuni SQL Server 2008, printre acesta numărându-se un sistem de „master data management” – un sistem de management central al datelor și al ierarhiilor dintre acestea. De asemenea, o îmbunătățire majoră a fost introducerea suportului pentru „Multi Server Management” – o consolă centralizată ce putea executa scripturi pe mai multe instanțe ale bazei de date.

Crearea unei tabele

O tabelă reprezintă un set de date, organizate și stocate sub formă de rânduri și coloane. Un tabel conține date referitoare la obiecte asemănătoare, spre exemplu angajați, firme etc.

Entitățile dintr-un model de date se leagă în general de tabele (3.2.1), în contextul bazelor de date. Atributele entităților se referă la coloanele tabelului (3.2.2).

Vom prezenta în cele ce urmează sintaxa necesară creeri unui tabel simplu, cu o singură coloană.

create table Firme (F_id int)

Fraza de mai sus crează un tabel cu numele Firme, având o singură coloană cu numele F_id, coloană ce poate stoca doar valori numerice de tip întreg. Acest tabel va accepta orice valoare întreagă, dar și valoarea NULL.

Valori implicite (default)

Când se inserează date în tabele, există posibilitatea ca la un moment dat să se introducă valori în mod automat. Spre exemplu, când se face listarea unui raport și există o tabelă ce ține evidența tuturor rapoartelor listate, cu siguranță va exista și o coloană ce va conține informații referitoare la data și ora listării. Această coloană se poate defini chiar de la început ca în cazul în care nu i se setează o valoare la inserare, aceasta se va completa în mod automat cu data și ora curentă a serverului.

Acest tip de tabel se poate construi după cum urmează:

CREATE TABLE LogListari

( NumeRaport VARCHAR(30),

NumeOperator VARCHAR(30),

DataListarii DATETIME DEFAULT(GETDATE())

)

Pentru a defini o coloană ce se autoincrementează (cel mai folosită pentru cheia primară), se poate folosi următoarea scriere:

CREATE TABLE LogListari

( ID_Listare INT IDENTITY(1,1) NOT NULL,

NumeRaport VARCHAR(30),

NumeOperator VARCHAR(30),

DataListarii DATETIME DEFAULT(GETDATE())

)

În fraza de mai sus se adaugă coloana ID_Listare, ce va porni de la valoarea 1 și se va autoincrementa automat cu o rație de 1.

Valorile NULL

Valoarea NULL reprezintă o valoare necunoscută. Spre exemplu, o tabelă ce se crează inițial și ulterior i se completează toate câmpurile: o tabelă cu angajați, poate inițial i se completează doar câteva valori, precum nume, prenume și cnp, iar ulterior i se vor completa datele contractuale.

Există și situații în care utilizatorul va vrea ca coloana să nu permită valoarea NULL. Cu alte cuvinte, acesta va fi obligat să adauge o informație în coloana respectivă. Spre exemplu toate coloanele chei primare sunt NOT NULL în mod evident.

Constrângeri

Constrângerile reprezintă reguli pe care datele ce se doresc a se introduce în celulele respective trebuie să le respecte:

UNIQUE – această constrângere nu permite adăugarea unei valori în celula respectivă în cazul în care această valoare există deja pe un alt rând, evident, în cadrul aceleiași coloane;

PRIMARY KEY – este similară constrângerii UNIQUE, însă exclude valoarea NULL. Cheia primară este întotdeauna indexată;

REFERENTIAL – această constrângere este folosită pentru a păstra integritatea bazei de date. Este folosită în cadrul relațiilor, făcând referință la o altă coloană. Cu alte cuvinte, nu se va permite adăugarea unei valori decât dacă aceasta există printre valorile coloanei referențiate;

CHECK – această constrângere ne asigură că datele introduse în coloană nu vor fi acceptate decât după ce vor trece testul specificat de CHECK-ul respectiv.

Microsoft SQL Server Management Studio

SQL Server Management Studio (SSMS) este un mediu integrat dedicat accesării, configurării, mentenanței, administrării și dezvoltării tuturor componentelor unui Server SQL. SSMS combină o cantitate imensă de unelte grafice împreună cu numeroase editoare de scripturi pentru a le pune la dispoziție dezvoltatorilor de toate nivelurile un mod ușor și intuitiv de utilizare.

Acesta cuprinde toate facilitățile care erau disponibile cu ultima variantă de SQL Server, precum Enterprise Manager, Query Analyzer (analizator de interogări) și Analysis Manager. În plus, acest tool funcționează împreună cu toate componentele SQL Server precum Reporting Services și Integration Services. Astfel dezvoltatorii se pot familiariza atât cu rolul de programator cât și cu rolul de administrator al bazelor de date.

SQL Server Management Studio reprezintă o aplicație ce funcționează pe baza unui Microsoft SQL Server (2005 sau mai nou), reprezentând principala unealtă de dezvoltare și manageriere a acestui server. Principala funcționalitate a acestui software o reprezintă Object Explorerul, ce permite utilizatorului să selecteze și să vizualizeze toate obiectele din cadrul unui server. Există și o variantă „express” a acestui produs software ce se poate descărca gratuit.

Figură 10 – Microsoft SQL Server Management Studio

În figura de mai sus, se poate observa în partea stângă Object Explorerul, unde toate obiectele bazei de date (sau doar o parte din ele, în funcție de drepturile pe care le are utilizatorul pe server) sunt vizibile într-o structura arborescentă, precum explorerul din Windows. Acest produs software este unul foarte puternic din punctul de vedere al dezvoltatorului, întrucât îi pune la dispoziție, pe lângă scrierea în mod tradițional a interogării și a executării acesteia, un mediu grafic ce facilitează createa tabelelor și tot ceea ce implică acest proces (creare de indecși, chei primare, constrângeri, defaulturi etc.).

Pentru dezvoltatorii locali, instalarea softwareului pune la dispoziție și instalarea unei instanțe SQL Server pe mașina de calcul locală. Am discutat anterior, în secțiunea 1.3.2.1, avantajele și dezavantajele unei astfel de baze de date, însă scopul includerii instalării unei instanțe SQL Server în instalarea acestui software nu poate decât să scoată în evidență interesul pe care compania Microsoft îl exercită față de dezvoltatori (piața lor țintă).

În contextul aplicației noastre, acest software a stat la baza întregului proces de programare, petrecând peste 50% din timpul alocat proiectului în acest mediu, proiectând, testând și îmbunătățind baza de date. Toate operațiile ce se fac asupra bazei de date sunt realizate cu ajutorul Procedurilor Stocate (Stored Procedures). Aceste proceduri sunt asemănătoare subprogramelor sau procedurilor din programarea tradițională. Modul lor de funcționare este foarte simplu și foarte sigur (dacă sunt programate corect): pe baza unui set de parametrii de intrare, aceste proceduri pot executa operațiile dorite asupra datelor, urmând să returneze un mesaj de ieșire, care va sugera succesul sau eșecul survenit executării acesteia. Vom discuta mai multe despre modul în care procedurile au fost gândite și despre rolul pe care acesta îl au în proiectul nostru în capitolele următoare.

Capitolul 5. Microsoft Visual Studio C#

Introducere

Limbajul de programare C# (pronunțat „C sharp”) este un limbaj creat cu scopul de a construi o largă varietate de aplicații folosint Frameworkul .NET. Acest limbaj este simplu de învățat, utilizat și foarte puternic orientat spre programarea bazată pe obiecte (orice entitate din C# este un obiect). Marele avantaj al C#-ului îl reprezintă chiar baza pe care acesta s-a clădit (nemuritorul limbaj de programare C, considerat și astăzi cel mai eficient și performant, însă mai greu de utilizat), permițând o dezvoltare rapidă a aplicațiilor, păstrând în același timp lizibilitatea și expresivitatea unui cod scris în limbajul C.

Visual C# este o implementare a limbajului C# de către compania Microsoft. Acesta pune la dispoziție o gamă largă de editoare de texte, compilatoare, designere, iar unul dintre cele mai importante avantaje ale acestuia îl reprezintă Debug-ul foarte ușor de folosit. Framework-ul mai sus menționat, în forma sa actuală (versiunea 4.5 – disponibilă cu versiunea 2013 a Visual Studio) pune la dispoziție librării pentru nenumărate funcționalități.

Mediul de programare

O privire de ansamblu

În cele ce urmează vom prezenta mediul de programare în care un programator C# își desfășoară o mare parte din timpul dezvoltării unei aplicații. Vom prezenta pe scurt componentele și toolurile pe care acest bogat și complex mediu de programare ni le pune la dispoziție.

În figura 11 – Visual Studio 2010 – Partea vizuală putem observa un mediu foarte bogat, ce poate fi copleșitor pentru un utilizator nou, dar cu timpul totul începe să aibe sens iar multitudinea opțiunilor pe care acest program le pune la dispoziție programatorului se vor dovedi mai mult decât folositoare. Vom identifica majoritatea componentelor, în cele ce urmează:

În partea de sus a imaginii putem observa o multitudine de butoane, acestea fiind complet customizabile, putând fi adăugate unele noi, sau ascunse, în cazul în care nu le folosim și ne ocupă spațiul degeaba. Rolul acestora este foarte diversificat, pornind de la butoane pentru salvarea modificărilor realizat într-un fișier, până la centrarea controalelor într-un container și rularea testelor;

În partea stângă a imaginii se observă o listă de obiecte, numită Toolbox. Această listă este indispensabilă unui designer de interfețe, întrucât conține toate elementele din care se poate crea o fereastră;

În partea dreapta – sus putem observa Solution Explorerul, o listă cu structură arborescentă, similară Object Explorerului din figura 10 – Microsoft SQL Server Management Studio.

Această listă ne oferă o privire de ansamblu asupra structurii fișierelor ce alcătuiesc proiectul;

Figură 11 – Visual Studio 2010 – Partea vizuală

În centrul imaginii putem observa Designerul, acesta este locul unde se crează efectiv interfața cu utilizatorul. Aceasta se poate crea cu ajutorul mouselui (prin „drag-and-drop”), sau prin programare, scriind de mână fiecare linie corespunzătoare creării interfeței (pentru ca Designerul să poată interpreta codul scris, acesta trebuie inserat în fișierul asociat acestei ferestre);

În partea din dreapta-jos se poate observa lista de proprietăți ale obiectului curent selectat din Designer. Acest panou cu proprietăți conține toate caracteristicile obiectului, iar de la butonul cu icoană de fulger se pot vedea evenimentele asociate fiecărei acțiuni asupra controlului respectiv.

Cele mai sus prezentate reprezintă partea vizuală a acestui mediu de programare. În cele ce urmează vom prezenta editorul de texte, folosindu-ne de figura 12 – Visual Studio 2010 – Editoare de cod. Putem observa faptul că textul este scris în culori diferite, acest lucru nu este întâmplător. Fiecare culoare are rolul de a scoate în evidență modul în care compilatorul privește acel cuvânt (clasă, variabilă, cuvânt cheie, comentariu etc.). Compilatorul este de mare ajutor chiar de la momentul în care codul este scris, întrucât acesta subliniază toate erorile cu roșu, specificând pentru fiecare eroare un mesaj sugestiv și de multe ori, chiar sugerând un mod de a corecta eroarea. Un exemplu rapid ar fi următorul: facem referire la o metodă pe care încă nu am definit-o, iar compilatorul ne va sugera să implementăm o metodă cu numele respectiv, iar dacă dăm click pe sugestie, o va genera automat pentru noi, specificând chiar și lista de parametrii, în cazul în care am încercat să o apelăm cu parametrii. Un alt exemplu ar fi uitarea folosirii unei directive using – scăpare foarte des întâlnită. Compilatorul ne va sugera directiva pe care am uitat să o declarăm și avem posibilitatea să îl lăsăm pe el să o declare pentru noi.

Figură 12 – Visual Studio 2010 – Editoare de cod

Putem observa cât de ușor de folosit este acest program și cât de folositor, în același timp. Spre deosebire de editoarele vechi care în cel mai bun caz ne indicau linia unde s-a produs o eroare, Visual Studio nu numai că ne indică exact locul erorii, dar ne spune și cauza precisă, de multe ori venind chiar cu sugestii referitoare la remedierea acesteia.

În cele ce urmează, vom insista puțin asupra anumitor componente mai sus descrise ce prezintă un grad mai ridicat de importanță în economia proiectului nostru.

Solution Explorerul afișează numele fișierelor asociate proiectului, alături de alte obiecte. Se poate da dublu-click pe numele unui fișier pentru a-l deschide în editorul de texte sau în designer (în funcție de tipul fișierului pe care se face click, programul îl va deschide în mod diferit).

Mai jos, în figura 13 – Solution Explorer se poate observa structura acestei componente, screenshot-ul fiind realizat chiar în proiectul nostru, pentru a ne obișnui din timp cu structura acestuia.

Solution Explorer

Solution 'InternshipsHR' – Acesta este fișierul cu nivelul cel mai înalt al soluției. Fiecare aplicație conține un singur fișier soluție. O soluție poate conține unul mai mai multe proiecte, iar în Visual Studio 2010 crează acest fișier pentru a ajuta la organizarea acestor proiecte. Extensia acestor fișiere este *.sln.

InternshipsHR – Acesta este fișierul ce conține proiectul C#. Fiecare fișier proiect face referință la unul sau mai multe fișiere ce conțin cod sursă sau alte obiecte necesare proiectului, precum imagini, iconițe și așa mai departe. O restricție care se impune ar fi următoarea: toate fișierele ce conțin cod sursă în cadrul aceluiași proiect trebuiesc scrise în același limbaj de programare. În Windows Explorer, putem identifica acest fișier cu numele de InternshipsHR.csproj.

Properties – Acesta reprezintă un folder în proiectul nostru. Dacă îl expandați (facând click pe săgețica din stânga acestuia), veți observa că conține un fișier cu numele AssemblyInfo.cs. Acesta este un fișier special care se poate folosi pentru a adăuga noi atribute programului, precum numele autorului, data la care programul a fost scris și asa mai departe. Se pot specifica atribute adiționale pentru a modifica modul în care programul funcționează. Explicarea funcționării acestora depășește scopul acestei lucrări.

References – Acest director conține referințe asupra librăriilor de cod compilat pe care aplicația noastră le poate folosi. În momentul în care codul C# este compilat, este convertit într-o librărie căreia i se asociază un nume unic. În Microsoft .NET Framework, aceste librării se numesc assemblies. Dezvoltatorii folosesc assemblieuri pentru a împacheta funcționalităti folositoare pe care aceștia le-au creat pentru a le putea distribui și altor dezvoltatori care ar putea dori să se folosească de acestea în propriile lor aplicații. Dacă expandați acest folder, veți vedea referințele implicite pe care Visual Studio 2010 vi le adaugă în proiect. Aceste assembliuri garantează accesul la multe caracteristici utilizate în mod comun de către Frameworkul .NET și sunt distribuite de către Microsoft împreună cu Visual Studio 2010/2013.

app.config – Acesta este fișierul de configurare al aplicației. Este opțional și ar putea lipsi câteodată. În cadrul acestuia se pot specifica setări pe care aplicația le poate folosi în momentul execuției pentru a-și modifica comportamentul, precum ce versiune de .Net Framework să folosească.

Program.cs – Acesta este un fișier ce conține cod sursă C#, și este afișat în cadrul editorului de cod și texte (fereastra principală) în momentul în care proiectul este creat. În cadrul acestuia se poate scrie cod, însă în momentul creării acestuia, Visual Studio va adăuga o porțiune de cod autogenerat.

Toolbox

În figura 14 – Toolbox ne este prezentată o componentă esențială oricărui designer de interfețe. Toolbox-ul ne prezintă sub formă de listă toate componentele grafice de care dispune Framework-ul folosit de către Visual Studio (versiunea din 2010 – .NET Framework 3.5, iar versile mai noi de 2013 – .NET Framework 4.0/4.5).

Modul de folosire al acestor componente este unul foarte simplu și intuitiv: folosind mouse-ul, putem selecta un element din lista respectivă, după care îl putem „trage” pe forma noastră, exact în locul în care dorim să apară. În cele ce urmează, vom prezenta câteva dintre cele mai comun folosite Controale:

Butonul – Reprezintă unul dintre elementele cele mai importante ale unei interfețe. Orice om care a folosit un calculator, cunoaște termenul de buton ca fiind: „un lucru pe care dacă apeși, ceva se întâmplă”. Exact asta și este, programatorii sunt cei care implementează funcționalitățile unui buton, însă designerul este cel care îl poziționează în cadrul ferestrei. După cum se poate observa în imaginea de mai sus, butonului i se asociază un Text, care trebuie să fie foarte descriptiv, pentru a-i indica utilizatorului efectul pe care apăsarea acestuia îl va avea. De asemenea se poate observa prezența unei imagini în cadrul acestui buton. De cele mai multe ori, imaginea are tot un rol descriptiv, ajutând la diferențierea butoanelor.

TextBox-ul – Reprezintă o componentă foarte des folosită în cadrul interfețelor grafice. În imaginea de mai sus, nu avem doar un textbox, ci avem și un Label asociat. Labelul este o altă componentă foarte folosită, însă nu o vom prezenta separat datorită simplității acesteia (este un Control ce afișează text). Aceste două compomente se folosesc împreună, astfel utilizatorul va știi cu ce anume să completeze textboxul. De asemenea, programatorul poate implementa numeroase funcționalităti asociate acestui Control, însă asta vom discuta în secțiunile viitoare.

ComboBox-ul – Această componentă este asemănătoare unui Text Box, întrucât ii permite utilizatorului să comunice cu aplicația, însă acest timp particular de ComboBox nu ne permite să scriem orice în cadrul lui, în schimb ne impune să alegem un element dintre cele afișate de acesta. Aceste Controale sunt extrem de folositoare în cazul în care vrem ca utilizatorul să selecteze unul și numai unul dintre elementele deja definite. De exemplu, dorim ca utilizatorul să aleagă o lună calendaristică, nu ne putem permite riscul de a-l lăsa să scrie singur Ianuarie, Februarie etc., în schimb, putem să populăm un combobox cu toate lunile anului, astfel utilizatorul nu va putea alege altceva decât ceea ce i-am pus la dispoziție.

DataGridView-ul – Reprezintă un tabel foarte bogat din punctul de vedere al customibilității și al funcționalității. Spre deosebire de tabelele tradiționale, folosind programarea, putem să specificăm ce tipuri de date dorim ca utilizatorul să introducă în celulele tabelului (cifre, litere etc.), îl putem lăsa să adauge rânduri noi sau îl putem obliga să completeze un tabel deja creat. Acest tabel se poate combina cu elementele de mai sus. Spre exemplu, în loc de o celulă tradițională în care nu se putea insera decât text, putem seta ca celula să fie de tip ComboBox. Astfel putem din nou specifica valorile cu care combobox-ul se populează și astfel putem controla mai bine datele pe care utilizatorul le adaugă în tabel.

DateTimePicker-ul – Acest control este folosit pentru a-i oferi utilizatorului un mod mai vizual și mai intuitiv de a introduce o dată calendaristică. Dacă facem click pe săgețica orientată în jos din partea dreaptă a controlului, acesta se va expanda și va arăta așa: de la săgețelele din stânga și dreapta putem schimba luna. Un alt lucru foarte folositor este acela că acest control ține cont de setările regionale ale calculatorului. Astfel formatul de afișare al datei (în imagine „zz.LL.aaaa” se poate schimba în funcție de formatul regiunii curente a sistemului de calcul). De asemenea, formatul și textul asociat zilelor se poate schimba (în funcție de limba setată). Cu alte cuvinte, programatorul nu va trebui să-și facă griji legate de modul în care controlul va arăta la clienții săi, întrucât acest lucru este supravegheat de către Framework.

Cele mai sus prezentate reprezintă o mică parte din elementele pe care Frameworkul .NET le pune la dispoziție utilizatorilor. Scopul acestui capitol a fost doar de a „zgâria suprafața” în ceea ce privește programarea vizuală în Visual Studio, urmând ca în secțiunile viitoare să vorbim și puțin de partea legată de programatori și de codare, întrucât până acum am vorbit de partea de design.

Programarea în Visual Studio C#

În cele ce urmează vom prezenta lucrurile din punctul de vedere al programatorului, spre deosebide re secțiunea anterioară, unde lucrurile au fost prezentate prin ochii unui designer.

Limbajul de programare C#, precum și predecesorul său C++ sunt limbaje care suportă programarea obiectuală. În C#, însă, s-a pus accentul foarte mult pe această programare pe obiecte, astfel fiecare entitate existentă în acesta este privită ca un obiect. Capitolul nu are rolul de a intra în detaliile programării obiectuale, ci de a prezenta modul în care Visual Studio ne permite să scriem cod într-un mod foarte simplu, efectuând foarte multe sarcini automate, pe care în mod normal, programatorul le-ar fi efectuat manual.

5.3.1. IntelliSense

InteliSense-ul este o caracteristica extrem de importantă a editorului de texte din Visual Studio, reușind să îi ușureze foarte mult munca unui programator prin sugerarea cuvintelor pe măsură ce codul este scris.

În figura din stânga putem observa că pe măsură ce scriem codul, această listă de sugestii va apărea sub textul scris. Această listă conține toate entitățile ce au sens în contextul respectiv. Așadar IntelliSense-ul nu va face sugestii lipsite de sens sau care vor genera erori. Acesta poate sugera orice, de la variabile de clasă (indicate cu albastru în figură), metode (indicate cu roz), funcții (indicate precum metodele, tot cu roz), până la evenimente (indicate printr-un fulger galben). Pe lângă utilitatea acestuia din punctul de vedere al sugestiilor, el poate imbunătății viteza de scriere a unui programator. Nu de puține ori un programator se confruntă cu greșeli de scriere în codul său, sau o scriere înceată, lucruri care îi distrag atenția și îl fac să piardă timpul. După cum se poate observa în imagine, elementul „tbAdresa” este selectat cu culaorea galbenă, ceea ce înseamnă că programatorul poate apăsa tasta Enter și tot textul va fi scris automat de către program; astfel dezvoltatorul nu va mai trebui să se chinuie să scrie perfect fiecare literă a variabilei respective. Așadar rolul acestui tool este extrem de important în economia mediului de programare, eliminând multe dintre erorile de scriere, ajutând în același timp programatorul să creeze cod într-un mod mult mai rapid și sigur.

Windows Forms

Deoarece formele (ferestrele/formularele) reprezintă unitatea de bază a aplicației noastre, este esential să numim câteva dintre funcționalitățile și designul lor. O formă este în esență o fereastră goală pe care programatorul o populează cu controale pentru a crea o interfață cu utilizatorul și în spatele cărora implementează un mod de tratare al informațiilor ce intră sau ies din GUI (Graphical User Interface). Astfel, Visual Studio ne pune la dispoziție un mediu de programare integrat (Integrated Development Environment – IDE) ce ușurează foarte mult scrierea codului, precum și o listă bogată de controale implementate în Frameworkul .NET. Prin acompanierea acestor controale cu codul scris în spate se poate crea o aplicație fără foarte mult efort.

Elemenetele prezentate la secțiunea 5.2.1 sunt cele mai semnificative elemente din cadrul unei aplicații de timp Windows Forms.

Capitolul 6. Aplicația de gestiune

Aplicația ce urmează să o prezentăm este o aplicație informatică ce se bazează pe elementele prezentate anterior în acest document, mai exact pe o bază de date Microsoft SQL Server, iar ca interfată grafică pe o aplicație de tip Windows Forms, dezvoltată în mediul Microsoft Visual Studio 2010 (C#). În acest capitol ne vom concentra asupra detaliilor privind aplicația în sine. Vom insista asupra modului de proiectare al bazei de date, precum și asupra modului în care am implementat interfața cu utilizatorul.

Aplicația în sine are la bază ideea de centralizare/gestionare a informațiilor referitoare la angajații, contractele acestora dar și informațiile aferente firmei din care aceștia fac parte. Toate informațiile sunt stocate pe un server unde se află baza de date (una singură pentru întreaga aplicație). În această aplicație se pot adăuga angajati noi, li se pot adăuga contracte care ulterior pot fi generate și listate.

Vom începe prin a prezenta baza de date, urmând ca în secțiunile viitoare să ne mutăm atenția spre partea de programare C#.

Proiectarea bazei de date

Vom începe prin a prezenta structura bazei de date. Întrucât aplicația are trei componente principale: Firme, Angajati și Contracte, vom începe prezentarea doar cu tabelele principale, pentru a facilita înțelegerea cât mai clară a ideii din spate și pentru a evita detaliile neimportante.

După cum se poate observa în figura 15 – Structura bazei de date (tabelele principale), structura bazei de date este una simplă și se bazează pe următorul principiu. Fiecărui tabel important (importanța se determină prin cantitatea și calitatea datelor ce sunt stocate în acestea) i se asociază un model de tip Master – Detail, altfel spus, vom avea un tabel ce conține ID-urile (codurile unice de identificare – cheia primară), acesta fiind tabelul Master și un tabel ce conține informațiile referitoare la acel ID – tabela de Detail. Acest model ne permite să vizualizăm într-un mod mai lizibil structura bazei, oferindu-ne un grad ridicat de separare între informațiile unei persoane, spre exemplu, și legăturile acestuia cu firmele și cu contractele aferente.

Din desen se observă în mod clar tabela principală a bazei de date: FirmeID. De această tabelă se leagă tabela Firme (tabela de tip Detail asociată), dar și tabela de angajați (AngajatiID), printr-o relație de tip One-To-Many, deoarece o firmă are mai mulți angajați. În cadrul aplicației noastre am exclus cazul în care un angajat este angajat la mai multe firme (caz în care am fi fost nevoiți să creăm o tabelă de asociere pentru a „sparge” relația Many-To-Many). În continuare, tabela AngajatiID este legată de tabela aferentă de tip Detail și de tabela ContracteID, printr-o relație de tip One-To-Many, întrucât un angajat are mai multe contracte, iar un contract nu poate aparține decât unui angajat. Completând în mod simetric modelul anterior prezentat, tabela ContracteID este legată de tabela de tip Detail asociată acesteia, Contracte.

Figură 15 – Structura bazei de date (tabelele principale)

În figura de mai sus se pot observa de asemenea și câmpurile fiecărui tabel. Vom continua prin a scoate în evidență logica din spatele denumirii coloanelor, dar și a tabelelor.

Reguli de denumire a entităților în baza de date

La baza acestor reguli stă un principiu simplu și foarte puternic: lizibilitatea. În momentul în care un utilizator al bazei de date dorește să creeze o interogare, aceste reguli își vor arăta puterea și importanța.

Denumirea tabelelor:

În primul rând, denumirea tabelei trebuie să fie reprezentată printr-un substantiv la plural. Acest lucru este sugerat în mod evident de bunul simț, deoarece un tabel conține informații legate de mai multe enități;

În al doilea rând, acest model de legare a tabelelor de tip Master – Detail necesită impunerea unei regului noi de denumire a tabelelor, pentru a putea deosebi aceste două modele de tabele între ele. Deoarece tabela de tip Master nu conține informații în sine, ci doar ID-uri, am considerat destul de sugestiva denumirea lor pe modelul următor: [nume_tabel] + ID. Așadar tabelul de tip Master asociat tabelei de Angajați se va numi AngajațiID;

În final, o altă regulă la fel de importantă ca cele precedente este aceea de a da tabelelor nume cât mai sugestive, chiar dacă denumirea lor va deveni astfel mai lungă și probabil mai greu de ținut minte, acest lucru se va dovedi foarte folositor pe viitor, când, inevitabil, va apărea necesitatea adăugării de noi tabele, iar în cazul unor reguli slabe de denumire a acestora, ne vom confrunta cu situația în care numele pe care dorim să îl atribuim unei tabele noi este deja asociat unei tabele mai vechi.

Denumirea coloanelor din tabele:

Prima regulă și cea mai importantă este de a prefixa fiecare coloană cu una sau mai multe litere obținute prin abrevierea numelui tabelei. Spre exemplu, tuturor coloanelor din tabela AngajatiID le-a fost asociat prefixul AI. Astfel, când dorim să scriem o interogare spre exemplu și nu știm exact numele coloanei, însă știm numele tabelei, vom putea deduce foarte simplu abrevierea acestui nume, adică chiar prefixul fiecărei coloane. Astfel IntelliSense-ul prezentat mai sus, ne va sugera exact coloanele aferente acelei tabele, întrucât fiecare dintre aceste coloane este prefixat de abrevierea numelui tabelei;

O a doua regulă este referitoare la denumirile coloanelor de tip Cheie Straina (Foreign Key). În tabela AngajatiID puteți observa coloana cu numele AI_FI_id. Acest nume s-a obținut prin atribuirea prefixului AI, specific tabelei AngajatiID, coloanei cheie primară provenită din tabela FirmeID, astfel, prin compunere (AI + FI_id) s-a obținut numele final al acestei coloane.

A treia regulă este asemănătoare cu ultima regulă din cadrul denumirii tabelelor. Numele coloanelor trebuie neapărat să fie cât mai sugestiv, deoarece adăugarea de coloane noi este mult mai ușoară și mult mai des întâlnită decăt adăugarea de tabele noi, astfel posibilitatea de a ne confrunta cu situația mai sus descrisă este mult mai mare.

Denumirea constrângerilor:

Precum aproape toate regulile de până acum au condus la adăugarea unui prefix la denumirea normală, acest lucru este valabil și aici. Vom descrie regulile prin câteva exemple. La denumirea unei chei primare, trebuie să avem în vedere următoarele aspecte: aceasta trebuie prefixată cu termenul PK (abreviere de la PrimaryKey), apoi numele coloanei pe care îl reprezintă. Spre exemplu, pentru coloana FI_id, constrângerea aferentă cheii primare se va numi PK_FI_id.

Referitor la cheile străine, regula se complică puțin. Spre exemplu, pentru coloana AI_FI_id din tabela AnagajatiID, numele asociat constrângerii cheii străine va fi FK_FirmeID_AngajatiID_AI_FI_id. Deși denumirea pare complicată, la bază stă o regulă simplă. Denumirea se compune astfel: FK + _ + [denumire_tabel_asociat] + _ + [denumire_tabel_curent] + _ + [nume_coloană_tabel_curent].

Regulile referitoare la alte constrângeri sunt mai simple, de exemplu o cheie de tip unique key pe o coloană se obține astfel: UK_[nume_coloană]. O constrângere de tip default se realizează la fel, doar că se prefixează cu DF și așa mai departe.

Regulile referitoare la denumirea funcțiilor și a procedurilor sunt relativ simple și vorbesc de la sine. Funcțiile și procedurile ce comunică cu interfața se precedează de prefixul GUI_, iar cele care nu comunică cu interfața și au rol de calcul, sau de lucru doar cu baza de date și sunt apelate manual de către programatori sunt precedate de DB_. La fel ca mai sus, denumirile acestora trebuiesc să fie foarte explicite și exacte.

Acestea sunt regulile pe care proiectul nostru le-a impus și le respectă în denumirea obiectelor unei baze de date. Deși dacă la început, folosirea lor ni se poate părea absurdă și oarecum exagerată, în timp s-au dovedit mai mult decât folositoare, oferind aplicației un grad ridicat de lizibilitate și chiar un grad ridicat de performanță, din punctul de vedere al vitezei de scriere al codului unei proceduri sau creării unui obiect, întrucât programatorii știau exact cum să identifice fiecare coloană în fiecare tabel.

O mare parte din proiectele ample, la care lucrează mai mulți oameni, au un astfel de set de reguli de denumire al obiectelor. Acesta poate fi unul diferit în funcție de echipă, însă foarte rar nu se pune accent pe denumirea entităților. De cele mai multe ori aceste reguli sunt stabilite de comun acord de către liderii de echipă împreună cu membrii acesteia, iar apoi scrise într-un loc unde fiecare membru (mai ales cei noi) să poată avea acces la ele pentru a le consulta oricând este nevoie.

Nevoia impunerii acestor reguli s-a bazat pe faptul că fiecare programator are propriile reguli de denumire, iar în cadrul unui proiect mare, la care lucrează mai mulți programatori, regulile vor ajuta la omogenizarea codului, astfel va părea că întregul proiect a fost scris de o singură persoană. Astfel, când un programator va citi codul unui alt programator, fiincă de la început ambii programatori au avut un set comun de reguli, acest programator va putea citi și înțelege cu ușurință codul colegului său.

Tabele de log

La baza supravegherii și a monitorizării activității utilizatorilor aplicației stau tabelele de log. În proiectul nostru, avem două astfel de tabele. Le vom prezenta în detaliu pe fiecare în parte:

Tabela ExecutionLog

La baza acestei tabele stă ideea de a păstra informații referitoare la fiecare apel de procedură ce interacționează cu datele bazei de date (insert/update/delete). În figura 16 – Tabela ExecutionLog, se pot observa coloanele acesteia, împreună cu câteva informații ce vă vor ajuta să vă faceți o idee cât mai clară și cât mai completă referitoare la scopul acesteia.

În esență, această tabelă reține informațiile referitoare la contextul în care o procedură a fost apelată, parametrii cu care aceasta a fost executată (EXL_Parametrii), numele procedurii (EXL_Name), tipul de efect pe care procedura îl are asupra bazei de date (EXL_Type), data și ora exactă la care s-a apelat procedura (EXL_Time), numele calculatorului (EXL_HostName), numele userului de domeniu (EXL_UserName) și modul în care s-a terminat execuția procedurii – cu eroare sau cu succes (EXL_Succes).

Figură 16 – Tabela ExecutionLog

Vom vorbi despre modul în care se inserează date în această tabelă în cele ce urmează.

Tabela ErrorLog

Tabela ErrorLog este foarte asemănătoare tabelei descrise mai sus. Logica care stă la baza acestei tabele este următoarea. În momentul în care execuția unei procedurii se sfârșește cu eroare (adică în coloana EXL_Succes a tabelei ExecutionLog se va regăsi textul Error), în momentul acela se va insera un rând în tabela ErrorLog, ce va conține informații referitoare la eroare, dar și ID-ul asociat din ExecutionLog. Pentru o mai bună înțelegere, consultați figura 17 – Tabela ErrorLog. Prima coloană reprezintă cheia primară a tabelei (EL_id), următoarele trei coloane (EL_ErrorLine, EL_ErrorNumber și EL_ErrorMessage) conțin informații referitoare la producerea erorii. Ultima coloană (EL_EXL_id) reprezintă cheia primară din tabela ExecutionLog, fiind cheie străină în acest tabel. Astfel putem să identificăm și informațiile referitoare la modul în care s-a apelat procedura, putând să determinăm cauza producerii erorii. În altă ordine de idei, dacă un operator cauzează o eroare, în baza de date va exista o dovadă în caz că acesta nu recunoaște greșeala făcută și dă vina pe aplicație (caz foarte des întâlnit).

Figură 17 – Tabela ErrorLog

Reguli de scriere și logica procedurilor

La baza creării unor proceduri sigure stă o tehnică corectă de programare. În cele ce urmează, vom prezenta codul unei proceduri de adăugare a unui angajat în aplicația noastră, deoarece cele mai simple și intuitive explicații se dau pe un exemplu.

Explicarea codului:

În prima parte, putem observa numele funcției DB_AddAngajat. Deși aceasta este apelată în mare parte din interfață, se apelează de multe ori și de către programatori, aceasta fiind una dintre cele mai importante proceduri, pentru a se diferenția de celelalte proceduri de gui, i s-a atașat prefixul DB (este singura excepție a regulilor discutate în secțiunea 6.1.1).

Pe rândurile următoare, se află parametrii funcției. Obervați că numele lor sunt aproape identice cu numele coloanelor din tabelă, mai puțin sufixul _new, care are rolul de a sugera faptul că informațiile sunt noi, urmând ca acestea să fie folosite la o acțiune de tip INSERT.

Comentariile sunt foarte importante în cadrul unui proiect mare. Acestea ajută la lizibilitatea și la înțelegerea codului de către ceilalți colegi din echipa de programare.

În partea imediat următoare sunt declarate variabilele locale, ce vor servi la inserarea unor valori în tabelele de log, prezentate în secțiunea 6.1.2.

Corpul procedurii începe cu setarea valorilor parametrilor în variabila @EXL_Parametrii_new, urmând ca aceștia să fie inserați în tabela ExecutionLog.

Partea interesantă a procedurii începe în corpul BEGIN TRY, precedat de instrucțiunea BEGIN TRANSACTION. Toate modificările ce se fac asupra datelor din baza de date sunt „înfășurate” de un set de instrucțiuni TRY–CATCH. De asemenea, toate modificările se fac simultan, într-o singură tranzacție. Explicație: deoarece acest insert nu se realizează într-un singur tabel ,întrucât adăugarea unui nou angajat, conform structurii explicate în secțiunea 6.1 și figura 15 – Structura bazei de date (tabelele principale), necesită adăugarea datelor în două tabele diferite: id-ul și firma pe care se angajează persoana se vor adăuga în tabelul AngajatiID, iar datele referitoare la persoană se vor adăuga în tabela Angajati, adăugarea se împarte în două etape:

Prima etapă: inserarea în tabelul AngajatiID. În cazul în care din interfață se trimit parametrii incorecți ce pot genera erori, nu se mai dorește continuarea procesului. Cu alte cuvinte, daca nu s-a realizat insertul în prima tabelă, tot procesul se oprește, lucru realizat folosind tranzacțiile (ROLLBACK).

A doua etapă poate presupune și ea erori, întrucât tabela Angajati este legată cu multe alte tabele (Nomenclatoare etc.) și se pot genera conflicte de chei străine. În cazul în care nu se poate realiza cu succes procedura, datele NU vor fi adăugate, iar utilizatorul va fi informat corespunzător.

În corpul tranzacției se poate observa apelarea unei proceduri denumite DB_InsertExecutionLog. Această procedură este cea care inserează informații în tabela de log. Codul aceste proceduri arată astfel:

După cum am explicat în secțiunea 6.1.2.2, în cazul în care procedura se apelează cu parametrul Error, această procedură va adăuga o nouă înregistrare în tabelul ErrorLog, împreună cu informațiile aferente erorii și cu codul liniei din ExecutionLog unde s-a produs eroarea.

În cazul în care procedura noastră nu întâlnește erori, datele sunt inserate în tabele prin intermediul unei singure tranzacții, iar utilizatorului i se reîncarcă ecranul cu datele noi introduse.

După cum se poate observa, codul este „presărat” cu tot felul de comentarii. Lucrul acesta este foarte important în cadrul unui astfel de proiect mare, la care lucrează mai mulți programatori. La fel se poate observa și secțiunea referitoare la scopul procedurii din prima parte a codului. Este important ca fiecare procedură să aibe un scop precis, iar aceste câteva rânduri pot ușura enorm munca unui alt coleg când citește codul procedurii.

Toate procedurile din baza de date prezentată în acest document respectă tiparul celei de mai sus, deci am putea spune că modul de scriere este unitar. Un alt lucru foarte important de specificat este acela că în codul C# al proiectului NU există cod SQL. De ce? Pentru că singurul mod de a scrie cod SQL într-un proiect C# (dacă facem abstracție de biblioteca ce facilitează lucrul cu expresii-lambda LINQ) este prin scrierea codului într-o variabilă de tip string. Astfel se pot strecura nenumărate greșeli de scriere, întrucât compilatorul aplicației Visual Studio nu interpretează textul din variabile. Așadar, orice operație se face asupra bazei de date, fie ea și doar de citire a datelor, se realizează prin intermediul unei proceduri.

În figura 18 – Proceduri se poate observa mulțumea de proceduri pe care baza de date prezentată o are, fiecare dintre acestea având un rol bine stabilit.

Vom face câteva mențiuni cu privire la denumirea procedurilor:

Cele prefixate de textul cb servesc la popularea controalelor de tip ComboBox prezentate anterior la secțiunea 5.2.3;

Cele prefixate cu DGV sunt folosite la popularea controalelor de tip DataGridView (tabelelor).

Cele prefixate de termenul Raport sunt proceduri ce furnizează date pentru generarea automată a rapoartelor din aplicație;

De cele mai multe ori, scopul procedurii apare chiar în denumirea acesteia:

DB_AddAngajat – Adaugă un nou angajat;

DB_InsertAndAddAngajat – Modifică un angajat, păstrând și istoricul.

Istoricul datelor

În orice bază de date, de-a lungul timpului, asupra informațiilor se fac nenumărate modificări, însă ce se întâmplă cu valorile vechi? Se pierd? În cazul lipsei conceptului de istoric așa se va întâmpla. Angajata Popescu Laura se poate căsători, iar numele ei de familie se poate schimba. Acest lucru implică o modificare a câmpului A_Nume în tabela Antajati. Însă cum se face modificarea?

În cele ce urmează vom prezenta conceptul de istoric al datelor precum și termenii de ValabilitateInceput și ValabilitateSfârșit.

După cum probabil ați observat deja, în fiecare dintre tabelele din figura 15 – Structura bazei de date (tabelele principale) se găsesc coloanele [prefix]_ValabilitateInceput și [prefix]_ValabilitateSfarsit. Dar ce reprezintă mai exact aceste coloane? Pentru început, aceste coloane sunt de tip date și au rolul de a prezenta intervalul de timp în care înregistrarea respectivă a fost valabilă. Așadar dacă angajata noastră Popescu Laura își schimbă numele de familie în Marin, înregistrarea curentă din tabela bazei de date NU se va modifica. Informațiile vor apărea în tabelă astfel:

Se poate observa faptul că înregistrarea „veche”, în care numele de familie al angajatei era Popescu nu s-a șters, nici măcar nu s-a modificat. Singura coloană care s-a modificat la această înregistrare a fost A_ValabilitateSfârșit. Tradus în limbaj natural, datele aferente angajatei au fost valabile până la data specificată în câmpul A_ValabilitateSfarsit. Datele actuale ale angajatei sunt cele care au drept valoare a coloanei de valabilitate sfârșit data 9999-12-31.

Acest mod de operare explică și numele procedurilor prefixate cu textul UpdateAndAdd… din figura 18 – Proceduri, deoarece o modificare nu implică doar o alterare a înregistrării în sine, ci și o adăugare a unei poziții noi.

Procedura care realizează această „modificare” verifică și data de valabilitate început, pentru a nu apărea anomali (data de sfârșit să fie mai mică decât cea de început). În cadrul procedurii, data de sfârșit a vechii proceduri se setează cu o zi înainte față de ziua curentă. În cazul de față, adăugarea s-a realizat chiar în ziua cu modificarea, deci nu s-a mai putut scădea o zi din valabilitatea de sfârșit pentru că ar fi apărut anomalia mai sus descrisă.

Așadar tabelele prezentate nu conțin doar simple informații aferente persoanelor din firme, ci și tot istoricul modificărilor pe care datele acestora le-au suferit de-a lungul timpului.

În mod natural, această strategie de salvare a istoricului în același tabel cu datele curente are și dezavantaje. Se pun următoarele două probleme:

Întrebare: Cum pot selecta din tabele datele cele mai recente ale angajatului?

Răspuns: Simplu, selectez rândurile care au valabilitatea de sfârșit '9999-12-31' .

Întrebare: Cum pot selecta din tabele datele pe care un angajat le-a avut într-o anumită lună?

Răspuns: Aici situația se complică, deoarece un angajat poate avea mai multe înregistrări valabile într-o anumită lună. Așadar trebuie să identificăm ultima înregistrare valabilă a angajatului în luna dată. Pentru preluarea acestor date, am creat funcții ce returnează tabele (Table-valued Functions). Codul aferent funcției de preluare a datelor unui angajat este următorul:

ALTER FUNCTION [dbo].[DB_Angajati]

(

@Data DATE,

@F_id INT

)

RETURNS @tmp_Angajati TABLE

(

ID INT,

[ID Angajat] INT,

[ID Firma] INT,

Nume VARCHAR(50),

Prenume VARCHAR(50),

CNP VARCHAR(13),

Marca VARCHAR(10),

[Data Nastere] DATE,

[Nr CI] VARCHAR(6),

[Serie CI] VARCHAR(2),

Nationalitate VARCHAR(30),

Judet VARCHAR(30),

Localitate VARCHAR(30),

Adresa VARCHAR(200),

Telefon VARCHAR(10),

[Unitate emitenta CI] VARCHAR(20),

[Data emiterii CI] DATE,

[Data expirarii CI] DATE,

Rectificativa BIT

)

AS

BEGIN

DECLARE @tmp TABLE

(

[ID Angajat] INT,

[ValabilitateInceput] DATE

)

DECLARE @tmp2 TABLE

(

[ID Angajat] INT,

[ValabilitateInceput] DATE

)

INSERT INTO @tmp

SELECT A_AI_id,

A_ValabilitateInceput

FROM Angajati INNER JOIN AngajatiID ON A_AI_id = AI_id

WHERE A_ValabilitateInceput <= @Data

AND AI_FI_id = @F_id

AND A_Dezactivat = 0

INSERT INTO @tmp2

SELECT [ID Angajat], MAX(ValabilitateInceput)

FROM @tmp

GROUP BY [ID Angajat]

INSERT INTO @tmp_Angajati

SELECT A_id,

A_AI_id,

FI_id,

A_Nume,

A_Prenume,

A_CNP,

A_Marca,

A_DataNastere,

A_Nr_CI,

A_Serie_CI,

NN_Nationalitate,

NJ_DenJudet,

NS_Numele_Localitatii,

A_Adresa,

A_Telefon,

A_UnitateEmitentaCI,

A_DataEmiteriiCI,

A_DataExpirariiCI,

A_Rectificativa

FROM Angajati INNER JOIN AngajatiID ON A_AI_id = AI_id

INNER JOIN NomenclatorNationalitati ON A_NN_id = NN_id

INNER JOIN NomenclatorSirute ON A_NS_id = NS_id

INNER JOIN NomenclatorJudete ON NJ_id = NS_NJ_id

INNER JOIN FirmeID ON FI_id = AI_FI_id

WHERE A_id IN (SELECT MAX(A_id)

FROM Angajati INNER JOIN @tmp2 ON A_AI_id = [ID Angajat]

AND A_ValabilitateInceput = ValabilitateInceput

GROUP BY [ID Angajat]

)

RETURN

END

Se poate observa faptul că procedura nu este una chiar foarte simplă, întrucât ne folosim de două tabele auxiliare pentru a putea filtra corect datele. În tabelul @tmp reținem ID-urile și Valabilitatea de Început a tuturor angajaților, urmând ca în tabelul @tmp2 să selectăm cea mai mare valoare (cea mai recentă/ultima din luna despectivă).

Acest tip de funcții ne permite să accesăm rapid informațiile unei persoane de la o anumită dată. Avem proceduri asemănătoare și pentru Firme și pentru Contracte.

În cele ce urmează vom vorbi despre modul în care permitem sau nu modificarea acestor date în trecut. Se pune problema următoare. Un utilizator al aplicației noastre vrea să modifice datele unei persoane în trecut cu 5 luni de exemplu. Cum acționăm în acest caz, permitem modificarea sau nu?
Despre asta vom discuta în secțiunea următoare.

Conceptul de lună închisă și rectificativă

Să spunem că unei persoane i se modifică salariu în luna Ianuarie, iar utilizatorul aplicației noastre își amintește (sau îi este atrasă atenția) în luna Martie că a uitat să opereze modificarea la timp. Acesta nu va putea opera în trecut (cel puțin nu în mod obișnuit).

În aplicația noastră am implementat conceptul de lună închisă. Acesta se bazează pe faptul că o lună odată încheiată, asupra acesteia nu se mai acceptă modificări. În baza noastră de date avem o tabelă numită Luni, tabelă ce ne indică dacă pe o animită firmă (să nu uităm că aplicația noastră funcționează simultan pentru mai multe firme) luna curentă este deschisă sau închisă.

În figura 19 – Tabela "Luni" putem observa structura și putem înțelege scopul acesteia. În linii mari, avem câte o înregistrare aferentă fiecărei luni, în fiecare firmă, care ne indică statusul acesteia (închisă sau deschisă, prin 1 sau 2 în coloana L_NTL_id–FK tabela NomenclatorTipLuni).

La fel putem observa conceptul de Valabilitate și în acest tabel.

Revenind la subiect, dacă este NEAPĂRAT NECESAR, aplicația va permite modificarea unei înregistrări din trecut printr-o rectificativă. Rectificativa este o modificare a înregistrărilor pe lunile închise. Implementarea acestei opțiuni s-a impus datorită proceselor câștigate în instanță de angajații care, din diverse motive, în urma cărora apărea necesitatea de a modifica date pe luni trecute (închise). Iar pentru a scuti programatorii de o modificare manuală a datelor prin comenzi SQL, s-a implementat acest concept. În momentul în care se realizează o rectificativă, coloana A_Rectificativă se va modifica din 0 în 1 (coloană de tip bit), ce ne va spune dacă înregsitrarea respectivă a suferit o rectificativă.

Această modificare se realizează cu ajutorul procedurii UpdateAndAddAngajat (Firma/Contract), care primește un parametru de tip BIT, care indică modul în care se va efectua modificarea.

Codul de unde reiese flexibilitatea funcției din punctul de vedere al tratării rectificativelor este următorul. Nu vom atașa tot codul procedurii întrucât nu acesta este scopul acestei secțiuni.

– Se realizeaza prima etapa din modificare: se modifica data expirarii la cea mai – recenta inregistrare a angajatului respectiv;

SELECT @A_ValabilitateInceput = A_ValabilitateInceput

FROM Angajati

WHERE A_id = @A_id

IF @A_Rectificativa = 'False'

BEGIN

IF @A_ValabilitateInceput = CAST(SYSDATETIME() AS DATE)

BEGIN

UPDATE Angajati

SET A_ValabilitateSfarsit = @A_ValabilitateInceput

WHERE A_id = @A_id

END

ELSE

BEGIN

UPDATE Angajati

SET A_ValabilitateSfarsit = DATEADD(dd, -1, CAST(SYSDATETIME() AS DATE))

WHERE A_id = @A_id

END

SET @A_ValabilitateSfarsit = '9999-12-31';

SET @A_ValabilitateInceput = GETDATE();

END

ELSE

BEGIN

SET @A_ValabilitateSfarsit = DATEADD(d, -1, (

SELECT TOP 1 MIN(A_ValabilitateInceput)

FROM Angajati

GROUP BY A_AI_id, A_ValabilitateInceput

HAVING A_AI_id = @AI_id

AND A_ValabilitateInceput > @A_ValabilitateInceput

)

)

IF @A_ValabilitateInceput = CAST((SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@A_ValabilitateInceput)+1,0)))AS DATE)

BEGIN

UPDATE Angajati

SET A_ValabilitateSfarsit = (SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@A_ValabilitateInceput)+1,0)))

WHERE A_id = @A_id

END

ELSE

BEGIN

UPDATE Angajati

SET A_ValabilitateSfarsit = DATEADD(d, -1,(

SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@A_ValabilitateInceput)+1,0))))

WHERE A_id = @A_id

END

SET @A_ValabilitateInceput = (SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@A_ValabilitateInceput)+1,0)))

END

Se observă cantitatea de cod necesară realizării acestui proces. Codul este foarte greu de citit și foarte greu de explicat așa că nu vom intra în detalii, ci vom discuta conceptual. Tot codul de sus nu face decât să seteze data de expirare a înregistrării respective, evident, în mod diferit, în funcție de valoarea parametrului @A_Rectificativa.

Acum putem spune că, în mare parte, am acoperit conceptele pe care se bazează structura bazei noastre de date, explicând în același timp și câteva funcționalități mai în detaliu. În cele ce urmează ne vom concentra pe partea de GUI a acestei aplicații. Veți observa că partea principală a aplicației noastre este reprezentată de baza de date. Interfața nu va face decât să „îmbrace” funcționalitatea deja prezentată a bazei de date, oferind utilizatorului un mod de a interacționa cu aceasta.

Interfața cu utilizatorul

În această secțiune ne vom îndrepta atenția spre partea vizuală a aplicației noastre, realizată cu ajutorul unui proiect de tip Windows Forms în mediul de programare Microsoft Visual Studio 2010, folosind limbajul de programare C#.

Vom încerca să prezentăm modul de funcționare al aplicației, designul acesteia dar vom și explica câteva bucăți de cod mai interesant și mai complexe.

6.2.1. O privire de ansamblu

În imaginea din partea stângă se poate observa structura proiectului nostru. În cele ce urmează ne vom concentra atenția asupra fiecărui fișier important, urmând să dăm exemple de cod și să explicăm funcționalitatea și scopul acestuia.

Pentru început, vom reține ecranele (fișiere de Form) principale care reprezintă principala formă de comunicare dintre utilizator și aplicație:

DialogLogOn – Este dialogul prin intermediul căruia utilizatorul se identifică și în urma unei autentificări reușite, acestuia i se permite accesul în aplicație.

FormAngajati – Reprezintă ecranul care permite vizualizarea dar și adăugarea de noi angajați, precum și ștergerea acestora.

FormContracte – Acest ecran servește la manipularea datelor legate de contractele angajaților (adăugare/modificare/ștergere). În acest ecran avem și opțiunea de a genera contracte.

FormDateFirma – Ecran folosit în vizualizarea istoricului datelor firmei, dar și un mod de a modifica datele acesteia.

6.2.2. Dialogul LogOn

În figura alăturată se poate observa designul ecranului de log on. Acesta este unul simplu și foarte intuitiv, fiind constituit din 5 controale. Două ComboBox-uri ce permit selectarea firmei și a utilizatorului cu care se dorește autentificarea.

Parola nu este vizibilă în timpul scrierii, exact ca în orice alt câmp de text specific introducerii parolelor.

La apăsarea butonului OK se apelează evenimentul aferent acestuia, eveniment ce realizează o apelul unei proceduri în baza de date.

Codul apelat la apăsarea butonului OK este următorul:

/// <summary>

/// Checks if the user name and the password are correct and closes the <see cref="DialogLogOn"/> form.

/// </summary>

/// <param name="sender"> The sender is <see cref="btOK"/>. </param>

/// <param name="e"> The event is <see cref="Click"/>. </param>

private void BtOK_Click(object sender, EventArgs e)

{

Globale.UserNameID = Convert.ToInt16(cbUsers.SelectedValue);

Globale.dataTable_Firme_Operatori = DBCon.Login(Globale.UserNameID, this.tbPassword.Text);

if (Globale.dataTable_Firme_Operatori.Rows.Count > 0)

{

Globale.UserName = this.cbUsers.Text;

Globale.Firm = this.cbFirms.Text;

Globale.RightID = (int)Globale.dataTable_Firme_Operatori.Rows[0]["D_ID"];

Globale.Password = this.tbPassword.Text;

this.DialogResult = System.Windows.Forms.DialogResult.OK;

this.Close();

}

else

{

MessageBox.Show(this, "Parola gresita!", "PRS", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

Se poate observa simplitatea codului. Majoritatea liniilor de cod de mai sus nu fac decât să seteze variabilele globale (din clasa statica Globale). Linia de cod care apelează procedura din baza de date este următoarea:

Globale.dataTable_Firme_Operatori = DBCon.Login(Globale.UserNameID, this.tbPassword.Text);

Se poate observa că se apelează procedura Login din clasa statică DBCon, clasă ce conține toate funcțiile ce apelează baza de date. Corpul funcțiilor din clasa responsabilă de conexiunea cu baza de date este adesea simplu și ușor de interpretat.

Vom prezenta în cele ce urmează funcția DBCon.Login:

/// <summary>

/// A function that executes a procedure from the data base due to login a user into his account.

/// </summary>

/// <param name="user_id"> The id of the user that tries to logon. </param>

/// <param name="password"> The password the user provides for the logon. </param>

/// <returns> The rights the user has on the corresponding firm. 1 – only viewing, 2- modifying, and 3 – admin. </returns>

public static DataTable Login(int user_id, string password)

{

DataTable dt = new DataTable("Firme_Operatori");

try

{

SqlConnection con = new SqlConnection(conString);

SqlCommand cmd = new SqlCommand("GUI_Login ", con);

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.AddWithValue("@EXL_UserName", Environment.UserName);

cmd.Parameters.AddWithValue("@O_OI_id", user_id);

cmd.Parameters.AddWithValue("@O_Password", password);

con.Open();

var dr = cmd.ExecuteReader();

dt.Load(dr);

dr.Close();

con.Close();

}

catch (Exception e)

{

System.Windows.Forms.MessageBox.Show(e.Message);

}

return dt;

}

Putem observa că fiecare funcție este documentată (partea de cod ce precedă procedura, scris cu culoarea verde reprezintă documentația funcției și este vizibilă la apelarea funcției prin intermediul IntelliSense-ului).

Funcția este una simplă. Primește doi parametrii, id-ul utilizatorului și parola introdusă de acesta. Apoi se inițializează conexiunea, se adaugă parametrii și se trimite spre execuție în baza de date. Procedura din baza de date va întoarce un tabel cu drepturile aferente utilizatorului.

Observație: Drepturile reprezintă cantitatea de informații la care un operator poate avea acces. Nu am insistat asupra acestui aspect în secțiunea anterioară pentru că politica drepturilor este aceeași în majoritatea aplicațiilor. În aplicația noastră există trei tipuri de drepturi: vizualizare, modificare și admin (ordonate crescător după importanță).

Revenind la subiect, în cazul în care procedura nu întoarce nici un rezultat, înseamnă că parola a fost introdusă greșit, iar utilizatorul este înștiințat prin intermediul liniei de cod următoare:

MessageBox.Show(this, "Parola gresita!", "PRS", MessageBoxButtons.OK, MessageBoxIcon.Error);

Această linie de cod va arăta utilizatorului un mesaj corespunzător, astfel acesta va știi motivul pentru care LogOn-ul a eșuat.

Pe acest principiu sunt construite toate ecranele. Utilizatorul va trebui să completeze formularul din ecran, ulterior acesta va apăsa un buton ce va efectua o interogare asupra bazei de date prin intermediul unei proceduri stocate în aceasta.

Vom continua prezentarea „containerul” ce conține cele trei ecrane principale în jurul cărora s-a construit întreaga aplicație.

6.2.3. Ecranul FormMain

Acest ecran reprezintă containerul principal al aplicației (fiind construit pe baza unui control de tip MdiParent – un control ce are rol de container pentru alte obiecte de tip form). Din meniul acestuia se deschid ecranele principale asupra cărora ne vom concentra atenția în secțiunile următoare.

La apăsarea unui item din lista cu Ecrane, se va apela evenimentul specific ce va lansa form-ul selectat. Codul din cadrul evenimentului DateFirmă este următorul:

private void ToolStripMenuItem_DateFirma_Click(object sender, EventArgs e)

{

if (this.toolStripMenuItem_DateFirma.Checked == false)

{

this.toolStripMenuItem_DateFirma.Checked = true;

this.formDateFirma = new FormDateFirma();

this.formDateFirma.MdiParent = this;

this.formDateFirma.Show();

this.formDateFirma.WindowState = FormWindowState.Maximized;

}

else

this.formDateFirma.Focus();

}

Se poate observa că în prima linie din cadrul acestui eveniment se verifică dacă nu cumva ecranul este deja deschis. În cazul acesta, ecranul este adus în fața celorlalte ecrane prin intermediul comenzii this.formDateFirma.Focus();

În cazul în care programul nu era deja deschis, se va crea un obiect al clasei FormDateFirma, după care i se va seta „părintele” ca find this, adică obiectul curent (acest container de tip MdiParent). Apoi se setează proprietatea WindowState a obiectului this.formDateFirma ca fiind FormWindowState.Maximized.

Acesta este rolul ecranului FormMain. Un rol precis, simplu de implementat și liber de erori (cel puțin în urma testelor efectuate asupra acestuia).

În secțiunile următoare vom discuta în parte de fiecare dintre ecranele pe care acest form principal le lansează prin intermediul meniului său.

Ecranul FormDateFirma

Figură 23 – Ecranul Date firma

În cadrul acestui ecran putem observa două părți distincte. Prima parte este reprezentată de jumătatea de sus a ecranului ce conține controale ce facilitează vizualizarea dar și modificarea datelor actuale ale firmei. A doua parte este reprezentată de jumătatea de jos a ecranului ce conține un control de tip DataGridView (cu alte cuvinte un tabel) cu istoricul modificărilor ce s-au efectuat asupra datelor firmei de-a lungului timpului. Acestea sunt read-only, adică nu se pot modifica prin intermediul tabelului.

Dacă dorim să efectuăm o modificare, spre exemplu vrem să schimbăm numele firmei. Apăsăm pe butnul Modifica, iar controalele din partea de sus vor deveni editabile. Pe măsură ce modificăm ceva în conținutul acestora, există un eveniment ce se apelează, eveniment ce verifică dacă valoarea nou introdusă de către noi coincide sau nu cu valoarea veche. În caz negativ, controlul se înroșește.

Figură 24 – Modificare în cadrul ecranului Date Firma

La apăsarea butonului salvare, valorile noi sunt salvate, iar fereastra este actualizată. Veți putea observa că s-a adăugat o nouă linie în tabelul de jos. Observați cu atenție modificarea Valabilității.

Înregistrarea anterioară „a fost expirată”, adică i s-a setat data de expirare ca fiind ziua anterioară a datei în care s-a efectuat modificarea, iar înregistrarea nouă a fost adăugată cu data de expirare infinită. De asemenea puteți observa că s-a actualizat și denumirea firmei din controlul Denumire.

De asemenea, în partea dreaptă a ecranului se poate observa butonul Raport. Acest buton generează un raport ce conține informațiile din tabelul de istoric.

La generarea rapoartelor am folosit bibliotecile de la Microsoft Report Viewer. În cadrul acestor biblioteci există ecranul pe care îl puteți vedea în partea stângă. Acesta este unul bogat în opțiuni, punându-ne la dispoziție salvarea raportului în format PDF, Excel sau Word, mărirea acestuia pentru o mai bună vizibilitate, precum și printarea acestuia direct din interfață, fără a fi nevoie să îl salvăm în prealabil.

De semenea, generarea unui astfel de document folosind biblioteca Microsoft.ReportViewer este foarte ușoară, bazată pe o strategie Drag-and-Drop, cum deja putem spune ca ne-au obișnuit cei de la Microsoft. Vom reveni asupra rapoartelor în secțiunile următoare.

Cam acestea sunt funcționalitățile ecranului Date Firmă. Este un ecran simplu, care nu a necesitat un efort prea mare în programare. Complexitatea acestui ecran nu este foarte ridicată, deși trebuie avută mare grijă la activarea și dezactivarea butoanelor în funcție de ce apasă utilizatorul. Pot apărea multe erori extrem de ușor. Iar astfel de erori pot afecta integritatea și calitatea datelor.

Ecranul FormAngajati

Acest ecran conține informațiile referitoare la angajații firmei. În cadrul acestuia putem adăuga, șterge (dezactiva) sau modifica datele unui angajat.

Figură 26 – Ecranul Angajati

Se poate observa gradul mai ridicat de complexitate al ecranului de mai sus. În partea cea mai de sus observăm două controale de tip ComboBox, reprezentând anul și luna pentru care datele din tabelul de sub acestea sunt relevante. În stânga tabelului putem observa câteva filtre, în cazul în care firma are foarte mulți angajați și identificarea acestora este greoaie. În jumătatea de jos a ecranului observăm datele angajatului selectat din tabelul de mai sus.

Despre acest ecran vom discuta mai mult, deoarece în spatele lui se află multe lucruri interesante legate atât de baza de date cât și de implementarea interfeței.

Ne vom concentra acum asupra butonului pe care scrie Modificare (butonul din mijlocul grupului de trei butoane plasate sub tabel). Acel buton își va schimba comportamentul în funcție de luna pe care se află setată valoarea ComboBox-ului. Dacă luna este deschisă (conform tabelului de luni despre care am vorbit în secțiunea 6.1.5), butonul ne va permite modificarea directă a înregistrării. În cazul în care luna este trecută, așadar închisă, pe buton va scrie Rectificativă și ni se va permite modificarea în trecut. În cazul în care se selectează o lună ce nu există în tabelul de luni (o lună viitoare, probabil din alt an) atunci butonul nu va mai permite modificarea, ci doar vizualizarea datelor.

De asemenea, dacă luna nu este deschisă, butonul de Stergere nu va mai putea fi apăsat, deoarece nu poti șterge un angajat decât dintr-o lună deschisă. Această flexibilitate a butoanelor și a modului în care acestea funcționează dau interfeței un caracter dinamic, având o importanță inestimabilă, deoarece utilizatorului nu i se lasă nici o șansă de a altera sau de a afecta integritatea datelor. Acesta este cel mai impotant aspect al interfeței noastre. Aceasta nu are doar rolul de a permite utilizatorului să interacționeze cu baza de date, ci are și rolul de a-l ghida pe acesta și de a-i pune la dispoziție toate informațiile, într-o formă inteligibilă, ca să își poată desfășura activitatea într-un cadru sigur.

Un alt lucru extrem de important este reprezentat de evenimentele care se apelează la modificarea datelor din TextBox-uri. Pentru a explica mai bine comportamentul acestor evenimente trebuie să vorbim mai întâi despre clasa ControlTag și despre expresiile regulate (Regular Expresion).

Vom începe prin a descrie clasa ControlTag:

public class ControlTag

{

public string DataSource { get; set;}

public string RegexPattern { get; set; }

public string DataTableName { get; set; }

public string ValueMember { get; set; }

public string DisplayMember { get; set; }

public bool IsRequired { get; set; }

public ControlTag(string DataSource)

{

this.DataSource = DataSource;

this.IsRequired = true;

}

public ControlTag(string DataTableName, string ValueMember, string DisplayMember)

{

this.DataTableName = DataTableName;

this.ValueMember = ValueMember;

this.DisplayMember = DisplayMember;

this.IsRequired = true;

}

public ControlTag(string DataSource, string RegexPattern)

{

this.DataSource = DataSource;

this.RegexPattern = RegexPattern;

this.IsRequired = true;

}

}

Această clasă este creată cu scopul de a asocia fiecărui control un set de informații folositoare în timpul execuției programului. Proprietatea Tag este o proprietate pe care fiecare Control din Framework-ul .NET o are. Această proprietate este de tip Object (superclasa din care toate clasele Framework-ului sunt derivate). În tagul fiecărui Control (fie el TextBox sau ComboBox) vom atașa un obiect al clasei ControlTag.

Vom discuta despre fiecare dintre câmpurile acestei clase în cele ce urmează.

Un alt lucru care trebuie menționat îl reprezintă expresiile regulate. Cu ajutorul acestora, am reușit să definim constrângeri referitoare la tipul de format pe care utilizatorul trebuie să îl respecte atunci când introduce date în câmpuri. Se poate spune că am efecutat o validare a datelor chiar de la momentul introducerii acestora. Expresiile regulate sunt foarte folosite în cadrul completării de formulare. Utilizarea acestora este destul de ușoară, odată ce programatorul a înțeles cum funcționează.

Tagurile controalelor sunt setate chiar din constructorul ecranului.

public FormAngajati()

{

this.InitializeComponent();

this.tbNume.Tag = new ControlTag("Nume");

this.tbPrenume.Tag = new ControlTag("Prenume");

this.dtpDataNasterii.Tag = new ControlTag("Data nastere");

this.tbCNP.Tag = new ControlTag("CNP");

this.tbMarca.Tag = new ControlTag("Marca", @"^\d{2}[A-Z]\d{4}$");

this.tbNrCI.Tag = new ControlTag("Nr CI", @"^\d{6}$");

this.tbSerieCI.Tag = new ControlTag("Serie CI", @"^[A-Z][A-Z]$");

this.tbAdresa.Tag = new ControlTag("Adresa");

this.tbTelefon.Tag = new ControlTag("Telefon", @"^0\d{9}$");

this.tbUECI.Tag = new ControlTag("Unitate emitenta CI");

this.dtpEmit_CI.Tag = new ControlTag("Data emiterii CI");

this.dtpExp_CI.Tag = new ControlTag("Data expirarii CI");

this.cbNationalitate.Tag = new ControlTag("Nationalitate");

this.cbLocalitate.Tag = new ControlTag("Localitate");

this.cbJudet.Tag = new ControlTag("Judet");

if (Globale.RightID.Equals(1))

{

this.btAdaugare.Visible = false;

this.btStergere.Visible = false;

}

}

Se observă utilizarea a doi constructori diferiți din cadrul clasei ControlTag, în funcție de caz, deoarece unele controale nu au un Pattern Regex pe care trebuie să îl satisfacă. Primul parametru al constructorului reprezintă numele coloanei din tabel a cărui informație o reprezintă controlul. Astfel, în momentul în care utilizatorul introduce date noi în cadrul acestui control, pot compara datele noi cu ceea ce există deja în tabel și astfel controlul își va schimba culoarea în cazul în care informațiile nu mai coincid (daca s-au făcut modificări).

Acum că am stabilit ce reprezintă clasa ControlTag și faptul că folosim expresii regulate pentru a valida datele pe care operatorul le introduce în formular, vom trece mai departe la explicarea evenimentului ce se apelează de fiecare dată când utilizatorul scrie ceva în controale.

private void TextModified_Control(object sender, EventArgs e)

{

if (this.rowDateAngajat != null)

{

Control c = (Control)sender;

ControlTag ct = (ControlTag)c.Tag;

string colName = ct.DataSource;

if (sender.GetType().Equals(typeof(DateTimePicker)))

this.Control_TextChanged((Control)sender, this.rowDateAngajat[colName].ToString().Substring(0, 10));

else

this.Control_TextChanged((Control)sender, this.rowDateAngajat[colName].ToString());

if (ct.RegexPattern != null)

{

if (!Regex.IsMatch(c.Text, ct.RegexPattern))

{

c.BackColor = Color.Yellow;

this.validation_error = Utilitare.VerifyChanges(gbModificare, 1);

}

else

{

if(this.changes.Equals(false))

c.BackColor = Color.White;

this.validation_error = Utilitare.VerifyChanges(gbModificare, 1);

}

}

}

else

{

Control c = (Control)sender;

ControlTag ct = (ControlTag)c.Tag;

if (ct.RegexPattern != null)

{

if (!Regex.IsMatch(c.Text, ct.RegexPattern))

{

c.BackColor = Color.Red;

this.validation_error = Utilitare.VerifyChanges(gbModificare, 1);

}

else

{

c.BackColor = Color.White;

this.validation_error = Utilitare.VerifyChanges(gbModificare, 1);

}

}

}

}

Acest eveniment este unic la nivelul controalelor (adică orice control ai modifica, tot acest eveniment se va apela). În cele ce urmează vom explica pe rând fiecare linie importantă de cod din cadrul acestui eveniment complex:

Pentur început, vom diviza codul acestui eveniment în două părți. Prima parte va fi reprezentată de instrucțiunile efectuate în cadrul verificării if (this.rowDateAngajat != null), iar a doua va fi reprezentată de codul ramurii else corespunzătoare instrucțiunii de mai sus;

Vom începe cu prima parte a codului. Ramura if sugerează faptul că ne aflăm în cazul în care utilizatorul modifică un angajat deja existent, deoarece dacă efectua o adăugare a unui angajat nou, toate datele existente la momentul respectiv în baza de date (null, deoarece un angajat nou nu are date deja existente în baza de date) erau reținute în variabila de tip DataRow this.rowDateAngajat. Se observă în mod evident că se „despachetează” obiectul de tip ControlTag din Tag-ul controlului sender (adică controlul care a apelat evenimentul). În urma obținerii tag-ului, se realizează o comparare între valoarea curentă a controlului și valoarea din baza de date (reținută în variabila rowDateAngajat). Această verificare nu este chiar atât de lipsită de sens, întrucât în cadrul acesteia se mai apelează o funcție din clasa statică Utilitare a proiectului nostru, clasă ce conține diferite metode folositoare în toate clasele. Acea funcție verifică dacă există cumva modificări în interfață, prin parcurgerea tuturor controalelor și verificarea dacă culoarea de fundal este albă. Apoi, în cazul în care tag-ului i s-a asociat un Pattern Regex, acesta se aplică textului și se colorează contorlul cu o altă culoare, sugerând faptul că textul introdus nu respectă regulile de scriere.

În partea a doua a evenimentului, se repetă pașii de la partea întâi, evitându-se partea cu compararea valorii noi cu cea deja existentă în baza de date.

Butonul din partea dreaptă (cel pe care scrie Raport) are rolul de a genera un raport ce conține informațiile persoanelor active în firmă la luna aleasă.

Figură 27 – Raport angajați

Pe prima linie a documentului puteți observa scris textul „Data valabilitatii datelor”. Tocmai pentru acest lucru am implementat tot sistemul acesta: să putem spune până la ce dată sunt valabile aceste date (întrucât raportul a fost printat în luna Iunie, datele angajatilor sunt valabile până la sfârșitul lunii).

Cam acestea sunt lucrurile pe care acest ecran le poate face. A fost un ecran complex, cu o capacitate de generare a erorilor imensă. Putem spune că la un astfel de ecran, testarea și remedierea erorilor durează mult mai mult decât implementarea în sine.

În cele ce urmează vom discuta despre ecranul de Contracte, un ecran cu o capacitate extraordinară de a genera contracte.

Ecranul FormContracte

Acest ecran conține informații referitoare la toate contractele persoanelor active în firmă. În cadrul acestui ecran nu mai avem ComboBox-uri de unde să selectăm perioada. Singurele contracte vizibile sunt cele active în prezent, întrucât legislația în vigoare nu mai permite vizualizarea/generarea de contracte decât dacă sunt actuale.

Figură 28 – Ecranul de contracte

Putem observa similaritatea acestui ecran cu cele precedente. Singura diferență majoră o reprezintă cel de al doilea tabel. Legătura dintre cele două tabele este de tip Master – Detail. În funcție de selecția primului tabel, al doilea tabel se va popula cu datele corespunzătoare angajatului selectat.

Evenimentele formularului sunt asemănătoare (dacă nu chiar identice) cu cele din ecranul cu angajați. Partea interesantă a acestui ecran o reprezintă generatorul de contracte. Dacă apăsăm pe butonul Generare contracte, ne va apărea o fereastră.

Această fereastră ne oferă posibilitatea de a ne alege persoanele pentru care dorim să se genereze contractele. În partea de sus regăsim filtre, un buton de golire a filtrelor, iar în partea de jos, în partea dreaptă avem un buton pentru selectarea tututor rândurilor din tabel („Select All”) și un buton pentru deselectarea rândurilor curent selectat („Clear”). În partea stângă se află buton „Raport” care generează efectiv raporturile.

Acest mecanism de selecție ne permite să generăm și ulterior să listăm simultan mai multe contracte.

De asemenea, există cazuri în care un angajat are mai multe contracte (legislația în vigoare nu permite unei firme să angajeze o persoană pe mai mult de 10 ore pe zi – ore cumulate în toate contractele sale active). Această verificare este și ea implementată în ecran, astfel un utilizator nu va putea introduce contracte ce îl vor obliga pe angajat să lucreze mai mult de 10 ore pe zi. În cadrul generatorului de contracte, în cazul în care persoana selectată are mai multe contracte active, acestea se vor genera toate odată.

Verificarea este foarte importantă deoarece adăugarea unui contract este destul de costitisoare din punctul de vedere al timpului unui operator și prin intermediul acestei atenționări, datele pe care acesta le-a introdus în ecran nu se pierd și poate remedia problema fără să fie nevoie să ia tot procesul de la început. Astfel de verificări fac diferența între o aplicație sigură și una care merge bine doar în cazul în care este folosită corespunzător. O aplicație gândită bine nu are cum să fie folosită necorespunzător, întrucăt aceasta va avea grijă să atenționeze operatorul în momentul în care acesta operează greșit.

Figură 30 – Atenționare contract deja existent

Această atenționare apare în momentul în care se apasă butonul de adăugare asupra unui angajat care are deja un contract activ. Aceasta nu are nici o influență asupra funcționării aplicației, ci are rolul de a înștiința utilizatorul că nu poate adăuga un contract ale cărui ore lucrătoare însumate cu cele deja existente să depășească 10.

Figură 31 – Atenționare total ore lucrătoare pe zi mai mare ca 10

Această eroare apare în urma verificării efectuate la apăsarea butonului de salvare. Scopul ei este de a interzice adăugarea contractului, însă datele pe care operatorul le-a completat în ecran nu se pierd, așadar poate corecta problema chiar după apariția erorii. Verificarea se face fără a accesa baza de date, ci din interfață.

Contractul generat din acest ecran este unul complex, care pe lângă datele pe care le preia din baza de date, mai primește și un parametru din interfață, referitor la drepturile operatorului care generează contractul (salariul va fi ascuns în cazul în care operatorul nu are drepturi suficiente).

În cele ce urmează vom prezenta cum arată contractul unui angajat odată generat. Acesta este suficient de mare încăt va ocupa două pagini.

Figură 32 – Contract individual de muncă pagina 1

Figură 33 – Contract individual de muncă pagina 2

Contractul de mai sus este în întregime generat automat, folosind informații din baza de date dar și informații din interfață.

Acestea sunt principalele ecrane care sunt folosite în interfața prezentată. Prin intermediul acestora, operatorilor li se oferă o cantitate destul de mare de automatizare și de gestionare a informațiilor. Într-o eră guvernată de calculatoare, toate marile firme și-au informatizat sitemele de gestiune, oferind astfel numeroase locuri de muncă programatorilor și designerilor de baze de date.

În cele ce urmează vom prezenta două clase din proiectul nostru asupra cărora am promis ca vom reveni.

Clasa Globale

Clasa statică Globale are rolulu de a reține informații referitoare la sesiunea curentă de lucru. Această clasă nu conține funcții, metode sau constructori, ci doar câmpuri publice, vizibile și accesibile din orice alt loc al aplicației noastre. Codul acestei clase este următorul:

public static class Globale

{

public static string UserName { get; set; }

public static int UserNameID { get; set; }

public static string Firm { get; set; }

public static int FirmID { get; set; }

public static int RightID { get; set; }

public static string Password { get; set; }

public static DataTable dataTable_Firme_Operatori { get; set; }

public static DataSet dataSetNomenclatoareEcranAngajati { get; set; }

public static DataSet dataSetNomenclatoareEcranContracte { get; set; }

}

După cum am spus și mai sus, rolul acesteia este de a stoca și de a pune la dispoziție informații legate de sesiunea curentă de lucru. S-ar putea spune că aceasta joacă rolul de „bază de date locală”.

Clasa Utilitare

Clasa statică Utilitare conține numeroase metode interesante ce sunt apelate pe tot parcursul proiectului. Aceasta conține metode generale de:

Populare a controalelor:

public static void FillControls(GroupBox gb, DataRow row, DataSet ds)

{

for (int i = 0; i < gb.Controls.Count; i++)

{

if(gb.Controls[i].GetType().Equals(typeof(TextBox)) || gb.Controls[i].GetType().Equals(typeof(MaskedTextBox)))

{

ControlTag tag = (ControlTag)gb.Controls[i].Tag;

gb.Controls[i].Text = row[tag.DataSource].ToString();

}

else if (gb.Controls[i].GetType().Equals(typeof(DateTimePicker)))

{

ControlTag tag = (ControlTag)gb.Controls[i].Tag;

try

{

((DateTimePicker)(gb.Controls[i])).Value = Convert.ToDateTime(row[tag.DataSource].ToString().Substring(0, 10));

}

catch (Exception e)

{

((DateTimePicker)(gb.Controls[i])).Value = Convert.ToDateTime(row[tag.DataSource].ToString().Substring(0, 9).Trim());

}

}

else if (gb.Controls[i].GetType().Equals(typeof(ComboBox)))

{

ControlTag tag = (ControlTag)gb.Controls[i].Tag;

if (tag.DataTableName != null)

{

try

{

gb.Controls[i].Text = row[tag.DataSource].ToString();

}

catch (Exception e)

{ MessageBox.Show(e.Message); }

}

}

else if (gb.Controls[i].GetType().Equals(typeof(GroupBox)))

{

GroupBox gb2 = (GroupBox)gb.Controls[i];

Utilitare.FillControls(gb2, row, ds);

}

}

}

Această funcție este folosită în foarte multe locuri, având rolul de a popula toate câmpurile dintr-un GroupBox, cu datele trimise ca parametru în variabila de tip DataRow. În cazul în care există GroupBox-uri imbricate, această procedusă se va apela chiar recursiv, pentru a trata fiecare GroupBox în parte. Această procedură folosește la populare informațiile din tag-ul fiecărui element.

În cadrul acestei proceduri se tratează separat cazurile în care controalele sunt de tipuri diferite, întrucât nu toate permit popularea în același mod.

Popularea controalelor de tip ComboBox:

public static void LoadComboBoxes(GroupBox gb)

{

for (int i = 0; i < gb.Controls.Count; i++)

{

ControlTag tag = (ControlTag)gb.Controls[i].Tag;

if (tag != null && tag.DataTableName != null)

{

try

{

Utilitare.LoadComboBoxFromDataTable((ComboBox)gb.Controls[i], Globale.dataSetNomenclatoareEcranContracte.Tables[tag.DataTableName], tag.ValueMember, tag.DisplayMember);

}

catch (Exception e)

{

MessageBox.Show(null, "A aparut o eroare la incarcarea datelor in campuri!\n" + e.Message, "InternshipsHR", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

}

}

Această procedură se ocupă de încărcarea tuturor controalelor de tip ComboBox dintr-un GroupBox. Acest lucru este posibil prin apelarea funcției Utilitare.LoadComboBoxFromDataTable:

public static void LoadComboBoxFromDataTable(ComboBox cb, DataTable dt, string valueMember, string displayMember)

{

cb.DataSource = dt;

cb.ValueMember = valueMember;

cb.DisplayMember = displayMember;

}

Puteți observa deja tiparul după care acest proiect a fost scris. Procedurile mici și cu scop precis reușesc să îi facă munca mai ușoară programatorului, oferind proiectului un grad ridicat de lizibilitate.

Procedură ce verifică dacă în cadrul unui GroupBox s-au efectuat modificări:

public static bool VerifyChanges(GroupBox gb, int verifyType)

{

Color color = verifyType.Equals(0) ? Color.Red : Color.Yellow;

for (int i = 0; i < gb.Controls.Count; i++)

{

Control c = gb.Controls[i];

if (!c.GetType().Equals(typeof(Label)) && c.BackColor.Equals(color))

{

return true;

}

if (c.GetType().Equals(typeof(GroupBox)))

{

if (Utilitare.VerifyChanges((GroupBox)c, verifyType).Equals(true))

return true;

}

}

return false;

}

Acest lucru este posibil verificând culoarea controlului. Dacă este diferită de alb, în mod clar avem fie un câmp modificat, fie un câmp completat incorect.

Procedură responsabilă cu transformarea controalelor needitabile în controale editabile:

public static void EnableControls(GroupBox gb)

{

for (int i = 0; i < gb.Controls.Count; i++)

{

if (!gb.Controls[i].GetType().Equals(typeof(Button)))

{

gb.Controls[i].Enabled = true;

}

}

}

Procedura inversă procedurii anterioare, însă această procedură este responsabiă și de golirea conținutului controalelor (în cazul în care acest lucru este specificat prin parametrul empty):

public static void DisableControls(GroupBox gb, bool empty)

{

gb.Enabled = false;

for (int i = 0; i < gb.Controls.Count; i++)

{

if (!gb.Controls[i].GetType().Equals(typeof(Button)) && !gb.Controls[i].GetType().Equals(typeof(Label))&& !gb.Controls[i].GetType().Equals(typeof(GroupBox)))

{

if (empty.Equals(true))

{

gb.Controls[i].Text = string.Empty;

if (gb.GetType().Equals(typeof(ComboBox)))

((ComboBox)(gb.Controls[i])).SelectedIndex = -1;

}

gb.Controls[i].BackColor = Color.White;

}

if (gb.Controls[i].GetType().Equals(typeof(GroupBox)))

{

GroupBox gr2 = (GroupBox)gb.Controls[i];

Utilitare.DisableControls(gr2, empty);

}

}

}

Funcție responsabilă cu validarea CNP-ului unei persoane:

public static bool ValidateCNP(string cnp)

{

if (cnp.Length != 13)

return false;

const string a = "279146358279";

long j = 0;

if (!long.TryParse(cnp, out j))

return false;

long suma = 0;

for (int i = 0; i < 12; i++)

suma += long.Parse(cnp.Substring(i, 1)) * long.Parse(a.Substring(i, 1));

long rest = suma – 11 * (int)(suma / 11);

rest = rest == 10 ? 1 : rest;

if (long.Parse(cnp.Substring(12, 1)) != rest)

return false;

return true;

}

Acest algoritm de validare al CNP-ului este cel oficial, oferind o verificare sigură a CNP-ului care de multe ori se poate introduce greșit.

Funcție responsabilă cu verificarea completării tuturor controalelor dintr-un GroupBox

public static bool CheckEmptyControls(GroupBox gb)

{

for (int i = 0; i < gb.Controls.Count; i++)

{

if (!gb.Controls[i].GetType().Equals(typeof(Label)))

{

if (gb.Controls[i].Tag != null)

{

ControlTag ct = (ControlTag)gb.Controls[i].Tag;

if (ct.IsRequired && gb.Controls[i].Text.Equals(string.Empty))

return false;

}

}

}

return true;

}

Și multe alte funcții/proceduri asupra cărora nu mai insistăm.

În cele ce urmează o să vorbim despre clasa ce furnizează accesul aplicației către baza de date.

Clasa DbCon

Această clasă statică are rolul de a face legătura aplicației cu baza de date. Toate procedurile bazei de date sunt apelate prin intermediul acestei clase. Nu vom pprezenta în detaliu fiecare funcție, deoarece acestea sunt specifice fiecărui ecran, însă vom prezenta funcțiile cele mai folosite și cele mai generale, întrucât majoritatea procedurilor sunt apelate sub aceeași formă și ar fi fost ineficient să facem câte o funcție separată care să facă același lucru. Astfel vom insista asupra procedurilor generale.

Funcția ce returnează un DataTable:

public static DataTable FillDataTable(string ProcedureName, Dictionary<object, object> Parameters)

{

DataTable dt = new DataTable(ProcedureName);

try

{

SqlConnection con = new SqlConnection(conString);

SqlCommand cmd = new SqlCommand(ProcedureName, con);

cmd.CommandType = CommandType.StoredProcedure;

if(Parameters != null)

foreach (KeyValuePair<object, object> param in Parameters)

{

if (param.Value == null)

cmd.Parameters.AddWithValue(param.Key.ToString(), DBNull.Value);

else

cmd.Parameters.AddWithValue(param.Key.ToString(), param.Value);

}

con.Open();

var dr = cmd.ExecuteReader();

dt.Load(dr);

dr.Close();

con.Close();

}

catch (Exception e)

{

Utilitare.ShowMessage(e.Message, Enum_tip_mesaj.Error);

}

return dt;

}

Se poate observa cantitatea de abstractizare prezentă în această funcție, întrucât aceasta nu știe nimic despre structura tabelului, nici despre ce fel de elemente conține dicționarul cu parametrii. Acest lucru face procedura foarte generală și ușor de folosit în numeroase situații.

Funcția ExecuteNonQuery

public static bool ExecuteNonQuery(string ProcedureName, Dictionary<object, object> Parameters)

{

bool ok = false;

try

{

SqlConnection con = new SqlConnection(conString);

SqlCommand cmd = new SqlCommand(ProcedureName, con);

cmd.CommandType = CommandType.StoredProcedure;

if (Parameters != null)

foreach (KeyValuePair<object, object> param in Parameters)

{

if (param.Value == null)

cmd.Parameters.AddWithValue(param.Key.ToString(), DBNull.Value);

else

cmd.Parameters.AddWithValue(param.Key.ToString(), param.Value);

}

con.Open();

var rd = cmd.ExecuteReader();

while (rd.Read())

ok = Convert.ToInt16(rd["OK"]) == 1 ? true : false;

con.Close();

}

catch (Exception e)

{

System.Windows.Forms.MessageBox.Show(e.Message);

}

return ok;

}

Această funcție efectuează un insert/update/delete pe baza de date, informând utilizatorul în momentul în care survine o problemă. Și despre această funcție putem spune că este destul de generală, singurul lucru harcodat aici este reprezentat de coloana denumită „OK” pe care procedura din baza de date trebuie să o întoarcă, cu una din valorile 0 și 1.

Odată cu descrierea acestei clase încheiem acest capitol dedicat în întregime aplicației noastre. În următorul (și ultimul) capitol vom trage concluziile referitoare la rolul pe care aplicația îl are și despre utilitatea acesteia în lumea reală.

Capitolul 7. Concluzii

În cadrul acestui capitol vom trage concluziile și vom vorbi despre contextul în care aplicația poate exista în economia unei organizații. Nu de puține ori, pornind de la o idee simplă sau de la un proiect cu scop didactic s-a ajuns la o aplicație reală, folosită pe o bază de date cu date reale și numeroase. Astfel se justifică prezentarea acestui capitol cu concluzii ce are rolul de a privi „în afara cutiei” în care un programator de cele mai multe ori își petrece viața.

7.1. Rolul aplicației

Aplicația are scopul de a asigura gestiunea mai multor firme. Prin gestiunea firmei se subânțelege gestiunea datelor acesteia cât și gestiunea angajaților ei, împreună cu contractele aferente acestora.

Aplicația poate fi foarte folositoare, întrucât toate marile organizații contemporane au adoptat sisteme informatice drept sisteme de gestiune, datorită faptului că acestea sunt mult mai eficiente din punct de vedere al timpului de acces la o informație. Cu vechile sisteme de gestiune, totul trebuia scris de mână și îndosariat, apoi aceste dosare (care de-a lungul anilor ajungeau la un număr considerabil) trebuiau depozitate într-un loc sigur, departe de umezeală, incendii sau orice alte evenimente ce le-ar fi pus în primejdie. De altfel putem spune că acest sistem era ineficient și din punct de vedere al siguranței datelor. După cum am spus și mai devreme, orice eveniment le putea distruge, iar copii nu existau. Deci o informație odata pierdută, nu se mai putea recupera, sau se recupera foarte greu. Astăzi se păstrează simultan mai multe copii ale bazelor de date, există backup-uri, iar informațiile pierdute se pot recupera fără prea mult efort.

După cum am spus și mai devreme, accesul la informațiile stocate era mult mai greu. Chiar dacă îndosarierea datelor era foarte bine concepută, timpul petrecut pentru a identifica o anumită informație era foarte ridicat. Noile sisteme asigura accesul la informație într-un timp record, fără prea multe bătăi de cap.

Putem astfel concluziona că rolul aplicației este unul foarte bine definit, întrucât orice mare organizație se poate folosi de aceasta drept aplicație de gestiune ale informațiilor aferente angajaților acesteia. Datorită faptului că aplicația funcționează simultan pentru mai multe firme, putem spune că rolul ei poate fi și mai important, fiind eficientă din punctul de vedere al unificării mai multor sisteme informatice (aferente mai multor firme) într-unul singur.

7.2. Utilitatea în practică

În practică, aplicația se poate dovedi foarte folositoare, însă la fel de bine se poate dovedi și ineficientă, întrucât o aplicație dată spre producție va lucra cu mult mai multe date decât a fost testată inițial și acest lucru se poate dovedi problematic. De aceea, majoritatea programatorilor își îndreaptă atenția și către performanță; atât a bazei de date cât și a aplicației.

Utilitatea acesteia este evidentă:

Aplicația va gestiona informațiile referitoare la firmele dorite, fiind capabilă să genereze în mod automat un raport privind datele acestora;

Aplicația va gestiona informațiile aferente tuturor angajaților firmei, fiind capabilă să genereze în mod automat un raport privind datele acestora;

Aplicația va gestiona și informațiile referitoare la contractele aferente ficărui angajat, oferint posibilitatea de generare a unui CIM (Contract Individual de Muncă) într-un mod complet automat.

Se poate spune că aplicaicația prezentată poate juca și rol de centralizator, întrucât aceasta poate gestiona simultan informațiile aferente unui întreg grup de firme, lucru extrem de folositor pentru organizațiile mari, care sunt formate din mai multe firme mai mici. Având toate informațiile la un loc în aceeași bază de date, se pot dezvolta mai departe diferite funcționalități. Spre exemplu se pot genera noi rapoarte cu statistici la nivelul întregii organizații folosind acele informații unificate în cadrul aceleiași baze de date.

În același timp acest lucru se poate dovedi ineficient întrucât cantitatea de informații stocate va crește proporțional cu numărul de firme pe care aplicația le va gestiona, dar acest aspect rămâne la latitudinea DBA-ului (Administratorului de Baze de Date), putând să folosească aplicația separat pentur fiecare firmă, folosind baze de date cu aceeași structură, însă localizate fizic pe servere diferite. Putem spune că aplicația noastră este flexibilă din acest punct de vedere.

De asemenea aplicația este flexibilă și din punctul de vedere al conceptului de drepturi pe care îl implementează. Astfel pot exista și operatori cu drepturi mai puține, neavând acces la informațiile sensibile. Acesta este unul dintre punctele forte ale aplicației, întrucât permite controlarea totală a accesului operatorilor la aplicație.

În concluzie, aplicația prezentată are un rol bine definit în lumea reală, deși, ca în orice aplicație folosită în cadrul unei organizații, drumul de la implementare și testare până la instalarea acesteia la clienți și funcționarea eficientă este foarte lung. Însă în cadrul acestui document am acoperit în mare partea de proiectare și implementare a unei aplicații cu scop bine definit.

Similar Posts