Facultatea de Economie si Administrarea Afacerilor
An 3, Informatic Economic Proiectarea Sistemelor Informationale 3. Modelul fizic Pn aici ne-am ocupat de: modelul conceptual, redat prin diagrama de clase ce includea entitile persistente. Ea a fost realizat folosind standardul UML i instrumentul Visual Paradigm. modelul logic al datelor, pentru care a fost adoptat modelul relaional i care a fost redat sub forma unei diagrame entitate-relaie. Acest model a fost obinut prin transformarea modelului conceptual. Acum ne vom ocupa de modelul fizic. El presupune implementarea modelelor conceptual i logic folosind un limbaj de programare i/sau un SGBD. Obinerea modelului fizic de persisten presupune dou transformri: 1) din diagrama de clase UML n clase folosind sintaxa limbajului de programare ales; 2) din diagrama entitate-relaie n tabelele bazei de date folosind SGBD-ul ales pentru implementarea bazei de date.
n ciclul de via al aplicaiilor moderne, aceste dou tranformri au loc ntr-o manier secvenial, conform schemei din figura 8. Prima faz presupune transformarea diagramei de clase UML n limbajul de implementare a aplicatiei, iar n faza a doua se obine schema bazei de date prin transformarea codului de implementare a claselor, rezultat n prima faz.
Figura 8 Ciclul de transformare a structurilor de persisten
Ambele faze de transformare pot fi automatizate sau efectuate manual. Astfel: 1) fiecare instrument de modelare UML ofer motoare de generare cod pe baza diagramelor (forward engineering) sau de obinere de diagrame pe baza codului existent (reverse engineering). 2) Limbajele moderne ofer cadre de lucru (frameworks) ORM (Object-Relational Mappings) care vor genera/sincroniza clasele de obiecte cu schema bazei de date pe baza informaiilor suplimentare asociate claselor (prin cele dou tehnici uzuale: adnotri sau fiiere XML). Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale Pentru implementarea modelului fizic am ales limbajul Java i framework-ul de persisten JPA (Java Persistence API) 1 , versiunea de implementare Hibernate 2 (open-source si utilizat pe scar larg in industrie). Trebuie reinut c, maniera general de abordare prezentat n continuare este perfect valid pentru orice alt limbaj de programare orientat pe obiecte. Astfel, far ndoial, o implementare .NET (VB sau C#) va fi 99% similar deoarece exist o implementare Hibernate si pentru .NET (vezi link-ul http://www.hibernate.org/).
3.1 Implementarea claselor (prima faz a transformrii)
Implementarea complet exemplificat pentru diagrama de clase din figura 2 este disponibil ntr-un proiect Eclipse (un mediu de dezvoltare foarte cunosctut utilizat si la disciplina Programare II) cu numele SGSM-Model. Pai de efecutat: 1) Dezarhivati continutul arhivei zip gasite pe Portal FEAA (disciplina PSI). Vei obine dou directoare cu cele dou proiecte: modelul (SGSM-Model) i o parte de implementare pentru interfaa grafic (SGSM-GUI). 2) Creai un nou Workspace n directorul radacin al celor dou proiecte. 3) Importai cele dou proiecte n respectivul workspace (File/import/General/ Existing projects into workspace/ selectai directorul rdcin/ bifai cele dou proiecte). 4) Motorul Hibernate este deja inclus n respectivul proiect (vezi directorul SGSM-Model/lib) iar bibliotecile de clase sunt ncrcate la nivelul proiectului (Project/Properties/Jaba build Path). Aceast operaiune, de import al bibliotecilor third parties utilizate de un anumit proiect este, evident, obligatorie.
La deschiderea proiectului SGSM-Model veti vedea o structur precum cea din figura 9.
1 Recomandam cu insisten o scurt revizuire a cunotinelor dobandite la discilina Programare II 2 http://www.hibernate.org/ Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale
Figura 9 Structura proiectului SGSM-Model
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale
n faza n care suntem acum, ne intereseaz doar clasele-entitate (pachetul ro.uaic.feaa.psi.sgsm.model.entities), care reflect ntocmai structurile din diagrama de clase din figura 2. PENTRU MOMENT, vom ignora adnotrile specifice ORM-JPA. La acestea vom reveni n seciunea urmtoare.
Pentru c structurile sunt foarte simple (se apeleaz la cunotine fundamentale de programare), nu vom descrie fiecare clas, ci doar cteva reguli importante privind deciziile luate n implementarea anumitor aspecte i codul aferent claselor respective. Astfel: 1) Toate clasele respect ablonul de lucru Java Beans, care reflect principiul ncapsulrii: a. Atributele sunt declarate cu vizibilitate private. b. Pentru fiecare atribut exista cte o metod set/get public (metode de acces si modificare cu scop de a oferi acces la valoarea atributului privat).
Sablonul utilizat blocheaza accesul direct la atribut. Cu alte cuvinte, un client NU va putea executa o secven de cod precum cea de mai jos (va obine erori de compilare din pricina specificatorului de vizibilitate private):
Document x = new Document (); x.dataOperare = new Date(); sau Date data = x.dataOperare;
Pentru a accesa valoarea atributului dataOperare, clientul va fi obligat s utilizeze metodele publice corespunzatoare:
x.setDataOperare(new Date()); Date data = x.getDataOperare();
Acest ablon este aplicabil n toate limbajele. Este recomandat a fi utilizat i, n fapt, este omniprezent n toate aplicaiile profesionale. Avantaje obinute sunt:
a) Este foarte uor de implementat un atribut de tip read-only. Astfel, s presupunem c, n exemplul de mai sus, dataOperare nu ar trebui s fie accesibil la modificare. Valoarea acestui atribut ar trebui s fie automat asociat la crearea unui obiect nou Document (fie prin constructor fie prin asocierea direct la nivelul declaraiei atributului), dup care va fi posibil doar citirea valorii, nu i modificarea ei. Implementarea este foarte simpl: dac nu declarm o metod public de modificare (setDataOperare), va fi practic imposibil modificarea valorii respective. b) Este foarte uor de adaugat cod suplimentar la momentul accesului sau modificrii valorii, fr vreo implicaie pentru client (nu va trebui s se modifice nimic la nivelul clientului). Astfel: Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale I. Presupunem c dorim implementarea unei restricii prin care data operare trebuie s fie mai mare sau egal cu data documentului. Vom modifica metodele setDataOperare si setDataDocument astfel nct s verifice aceast restricie i s genereze o eroare n caz de nclcare a regulii (foarte simular conceptului de trigger la nivelul bazei de date). II. Presupunem c dataOperare trebuie s fie prezentat public sub form de dat calendaristic dar intern trebuie stocat prin intermediul a trei atribute zi, luna, an. Pentru a implementa o asemenea cerin: i. Se declar 3 atribute private de tip Integer: zi, luna, an ii. Metoda public getDataOperare se modifc pentru a construi i returna un obiect Date pe baza celor trei atribute interne iii. Metoda public setDataOperare se modifc pentru a extrage din parametrul de tip Date primit cele trei pari componente: zi, luna, an.
2) Implementarea claselor i a relaiilor M:1 unidirecionale. Singurul aspect de remarcat n astfel de situaii este includerea atributului gestiune de tip Gestiune pentru implementarea navigabilitii dinspre clasa Consum spre clasa Gestiune.
public String getNumarComanda() { return numarComanda; } public void setNumarComanda(String numarComanda) { this.numarComanda = numarComanda; } public String getLocConsum() { return locConsum; } public void setLocConsum(String locConsum) { this.locConsum = locConsum; } public Gestiune getGestiune() { return gestiune; } public void setGestiune(Gestiune gestiune) { this.gestiune = gestiune; }
}
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale 3) Implementarea relaiilor 1:M bidirecionale. Atributele cu multiplicitatea mai mare de 1 se reprezint sub forma coleciilor. n cazul relaiilor 1:M bidirecionale, implementarea navigrii relaiei dinspre partea 1 (DocInsotitor) spre partea Multe (Receptie) impune includerea n clasa DocInsotitor a unui atribut de tip Receptie, care va avea multiplicitatea mai mare ca 1 i care va fi implementat ca o colecie. Coleciile respect un anumit ablon de implementare, astfel (lum exemplul relaiei ntre DocInsotitor si Receptie): a. Colecia se reprezint intern folosind tipul cel mai convenabil prin intermediul unui atribut private. In cazul de fa este folosit o implementare de tip Set, din dou raiuni: i. Intr-o colecie de tip Set obiectele sunt pstrate garantat ntr-o ordine bine definit (vezi cursul Programare II). ii. Implementarea JPA Hibernate recomand acest tip de colecie pentru relaiile One-To-Many (da, acest aspect este important n seciunea urmtoare, dar venim s-l descriem aici). b. Colecia este apoi expus public prin intermediul a cel puin trei metode: i. Dou metode pentru adugare/tergere elemente n/din colecie. ii. Gestionarea relaiei inverse (dinspre copil spre printe). iii. O metod de citire a coleciei ce returneaz o clon a obiectului. ATENIE clientul nu trebuie niciodat s poat modifica n mod direct numrul de elemente al coleciei (vezi restriciile de implementare de mai sus privind managementul relaiilor inverse). Ca urmare, vom expune colecia sub forma unui tip uzual (List) dar de tip read-only.
Urmrii cu atenie n exemplul reinut mai jos urmtoarele aspecte: a) Cele 2 metode de actualizare a atributului receptii de tip colecie: addReceptie i removeReceptie. b) n cadrul celor 2 metode se gestioneaz i relaia invers prin invocarea metodei setDocInsotitor din clasa Receptie. c) Metoda getReceptii care furnizeaz lista recepiilor pentru un document nsoitor sub forma unei liste read-only.
@Entity public class DocInsotitor extends Document { private String mijlTransport;
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale // ---------------metode pentru managementul colectiei receptii-----//
public void addReceptie(Receptie receptie) { this.receptii.add(receptie); receptie.setDocInsotitor(this); }
public void addReceptie(Gestiune gestiune) { Receptie r = new Receptie(); r.setGestiune(gestiune); this.addReceptie(r); }
public List<Receptie> getReceptii() { return Collections.unmodifiableList(new LinkedList(this.receptii)); }
}
@Entity public class Receptie extends Document{ @ManyToOne DocInsotitor docInsotitor; @ManyToOne Gestiune gestiune;
Boolean facturaPrimita;
public DocInsotitor getDocInsotitor() { return docInsotitor; } public void setDocInsotitor(DocInsotitor docInsotitor) { this.docInsotitor = docInsotitor; } }
S mai spunem c relaiile de agregare sunt implementate de regul ca fiind navigabile n ambele sensuri.
4) Implementarea relaiilor M:M bidirecionale. Implementarea acestor relaii este asemntoare cu situaia anterioar doar c ambele atribute ce vor implementa relaia de asociere vor fi de tip colecie. n aplicaia noastr nu exist o astfel de situaie ns, pentru exemplificare, vom analiza relaia ntre clasele BunMaterial i Categorie i n care vom considera c un bun material se poate regsi n mai multe categorii, iar o categorie va include mai multe bunuri materiale.
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale public class BunMaterial { ... private Collection<Categorie> categorii=new HashSet<Categorie>();
public void addCategorie(Categorie c) { if(! categorii.contains(c)) { categorii.add(c); c.addBunMaterial(this); } }
public void removeCategorie(Categorie c) { if(categorii.contains(c)) { categorii.remove(c); c.removeBunMaterial(this); } } }
public class Categorie {
private String denumire; private Collection<BunMaterial> bunuri = new HashSet<BunMaterial>();
public void addBunMaterial(BunMaterial bun){ if (!this.bunuri.contains(bun)) { this.bunuri.add(bun); bun.addCategorie(this); } } public void removeBunMaterial(BunMaterial bun){ if (this.bunuri.contains(bun)) { this.bunuri.remove(bun); bun.removeCategorie(this); } } public Collection<BunMaterial> getBunuri() { return Collections.unmodifiableCollection(bunuri); } }
3.2 Maparea claselor i a tabelelor bazei de date - tehnici ORM (a doua faz de transformare)
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale n aceast seciune vom realiza cea de-a doua transformare pentru obinerea modelului fizic, respectiv din codul de implementare a claselor (limbaj) n schema bazei de date. n acest scop se folosete un cadru de lucru ORM.
In seciunea de fa rmnem la modelul fizic i proiectul Java SGSM-Model descris n paragraful anterior. Spuneam ca folosim cadrul de lucru JPA (Java Persistence API) si implementarea Hibernate, o implementare care este disponibil att pentru Java ct i pentru .NET. Astfel, schema fizic a bazei de date prezentate n figura 3 poate fi creat n mod automat de catre motorul JPA (n cazul de fa, Hibernate) prin transformarea modelului obiectual Java n relaii corespunztoare modelului relaional.
Paii de parcurs pentru a genera schema bazei de date sunt urmtorii: 1. Se adauga informaiile suplimentare de persisten la nivelul claselor-entitate. In exemplul nostru am folosit metoda adnotrilor JPA, metod care presupune adugare de semantic JPA direct la nivelul clasei Java (n loc de fiiere de proprieti/XML separate) i descris la disciplina Programare II. 2. Se adauga fisierul META-INF/persistence.xml contine descrierea conexiunii catre baza de date. In exemplul nostru (vezi proiectul SGSM-Model) folosim baza de date PostgreSQL cu numele postgres (implict la instalare), instalata local, si o schema dedicata acestui exemplu cu numele sgsm (user si parola acelasi sgsm). i) In cazul in care doriti sa folositi o alta baza de de date PostgreSQL (spre exemplu cea pusa la dispozitie de faculate pentru disciplina BD I), va trebui sa modificati proprietatile: <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/postgres" />
ii) In cazul in care doriti sa folositi un alt motor de baze de date va trebui sa modificati, pe langa proprietatile de mai sus, si tipul driverului si dialectul SQL utilizat. Atentie, in functie de motorul bazei de date, va trebui sa descarcati driverul de pe Internet si sa- l adaugati ca dependinta la proiect (driverul PostgreSQL este deja asociat proiectului) iii) A se observa ca proiectul foloseste valoarea "update" 3 pentru proprietatea esentiala ce va instrui comportamentul generatorului de schema de baze de date: <property name="hibernate.hbm2ddl.auto" value="update" /> Aceasta valoare va asigura actualizarea schemei bazei de date la fiecare modificare a modelului obiectual.
3. TRANSFORMAREA efectiv i generarea schemei bazei de date, n cazul unui proiect POJO ca al nostru, va avea loc la execuia primei comenzi JPA-QL (este vorba de un SQL adaptat pentru nevoile modelului obiectual de asemenea prezentat la disciplina
3 Vezi si http://stackoverflow.com/questions/438146/hibernate-question-hbm2ddl- auto-possible-values-and-what-they-do Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale Programare II). Astfel, este sufficient s scriei o clas de test n care metoda main ar avea urmatorul continut:
4. public class FirstTest{ 5. public static void main(String[] args){ 6. 7. EntityManager em = Persistence.createEntityManagerFactory( 8. "SGSMPersistenceUnit").createEntityManager(); 9. em.createQuery("Select loc from Localitate loc").getResultList(); 10. 11. } 12. }
Nu vom insista aici asupra elementelor clasice JPA precum @Entity, @ManyToOne, @OneToMany. Toate acestea le aveti deja explicate in cursul Programare II pe care v recomandm s-l revizuiti cu atentie (sectiunea JPA, in special). Referitor la acestea, precizm doar c s-a urmrit simplificarea la maximum a codului, astfel evitnd declararea explicit a denumirilor de tabele i atribute. Ca urmare, n schema generat vom regsi exact aceleai denumiri ca i n modelul obiectual. Aceast stare de fapt poate fi, evident, uor de schimbat prin utilizarea atributelor corespunztoare la nivelul adnotrilor.
n continuare dorim doar sa nuanm anumite decizii de proiectare: 1) In primul rand, observam ca toate clasele entitate extind super-clasa AbstractEntity. Aceast clas este marcat @MappedSuperclass care instruiete motorul JPA s nu genereze o tabel distinct ci s includ toate atributele clasei n tabelele generate pentru fiecare din sub-clasele acesteia. Prin intermediul acestei strategii sunt atinse urmtoarele aspecte critice privind proiectarea unui model JPA: a) Toate entitile trebuie s dein un ID (cheia primar) si este recomandat ca aceast cheie s fie generat pe baza unei secvene n loc de a fi preluat din modelul domeniului. In spe, o dat asociat, utilizatorul NU trebuie s poat modifica aceasta valoare a cheii primare. De asemenea, trebuie evitate cu orice pre cheile compuse, acestea genernd probleme deosebite n managementul general al aplicaiilor client-server. Ca urmare, id-ul entitilor este delcarat in superclas i va fi motenit de subclase. Toate celelalte chei candidat din model vor fi declarate prin atributul unique (exemplu: Localitate.cod) care va determina generarea unei restricii de unicitate la nivelul atributului. b) Toate entitile trebuie s prezinte informaii generale privind: data crearii si modificarii, utilizatorul care a efectuat modificarea etc. Aceste atribute sunt, de asemenea, localizate la nivelul superclasei, unele dintre ele deinnd valori implicite. c) Pentru c n aplicaiile orientate pe date se folosesc in mod intensiv colecii de obiecte, toate entitile trebuie s implementeze n mod corect operatorii equals() i hashCode() pe baza ID-ului. Iat de ce aceti operatori vor fi implementai o singur dat la nivelul superclasei AbstractEntity. d) Toate entitile trebuie s ofere suport pentru mecanismul OptimisticLocking (crucial in aplicaiile client-server). Hibernate ofer o implementare standard n Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale acest sens pe baza atributului marcat @Version (n cazul nostru, atributul cu acelai nume). e) In final, scopul fundamental al superclasei AbstractEntity este de a furniza structura i comportamentul comune tuturor entitilor, oricare ar fi acestea, n funcie de necesiti. 2) Relatiile de compunere din diagrama de clase, ce reprezint modelul conceptual de structura, (figura 2) se traduc INTOTDEAUNA prin relaii JPA de tip @OneToMany bidirecionale (vezi DocumentLiniiDocument, unde exist reciprocitatea @ManyToOne n clasa-copil. Relaiile de asociere simple, unidirecionale (vezi ReceptieGestiune) se traduc doar prin relaii de tip ManyToOne la nivelul clasei- copil. Asemnarea cu mecanismul cheilor straine, in acest caz, este evident, singura diferen fiind aceea c n modelul relaional referina se face prin valoare in timp ce in moelul obiectual, prin pointeri. Astfel, urmtoarele dou propoztii sunt echivalente : a) SQL: select g.denumire from Receptie r inner join Gestiune g on r.idReceptie=g.id b) OO: receptie.getGestiune().getDenumire(); unde receptie este o variabil de tip Receptie. (inner join este echivalent cu navigabilitatea prin operatorul .) 3) Pentru relaiile de motenire, trebuie stabilit strategia de implementare la nivelul bazei de date. Exist trei strategii: 1) o singur tabel pentru toate clasele (atributele claselor-frunz vor avea valori null pentru tipurile vecine) variant care ofer optimizarea maxim a interogrilor dar mai puin ortodox pentru evanghelitii modelului relaional; 2) relaii 1:1 ntre super-clas i sub-clase cele mai corecte din punct de vedere al modelului relaional determin necesitatea unor operaii de tip Join frecvente i, ca urmare, o vitez de procesare inferioar variantei 1; 3) cte o tabel pentru fiecare clas-frunz determin multiplicarea cmpurilor comune in tabele multiple i necesitatea unor operaii asambliste de tip UNION n cazul interogarilor pe tipurile-printe. In exemplul de fa s-a luat decizia adoptrii stategiei 2. Aceasta abordare va determina generarea urmtoarelor relaii 1:1 n BD: Document cu DocInsotitor, Receptie i Consum.
In final, vom descoperi ca modelul generat automat este putin diferit fata de cel din figura 3, avand in vedere elementele de rafinare descrise mai sus, dar elementele generale raman aceleasi.
In continuare prezentm partea de cod de implementare a claselor care conine adnotrile JPA i implementarea clasei AbstractEntity.
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = -4803471783122679780L;
public static long getSerialVersionUID() { return serialVersionUID; }
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") protected Long id;
/** * the @Version is used for Optimistic Locking mechanism */ @Version @Column(name = "version") private Integer version; private String createdByUser; private String updatedByUser;
/** * most used for inheritance strategies */ private String entity_type = this.getClass().getSimpleName();
/** * The default constructor is always needed for JPA. */ public AbstractEntity() { super(); }
public AbstractEntity(Long id) { this(); this.id = id;
}
/** * Provide the right equals implementation. Really mandatory. This * implementation is based on the object's ID. However, if the object has * not been yet serialized to the DB, hence, no ID, then provide the usual * implementation. */ @Override public boolean equals(Object obj) { if (id == null) return super.equals(obj); if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale return false; final AbstractEntity other = (AbstractEntity) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; }
/** * Provide the right hashcode implementation. Really mandatory. This * implementation is based on the object's ID. However, if the object has * not been yet serialized to the DB, hence, no ID, then provide the usual * implementation. */ @Override public int hashCode() { if (id == null) return super.hashCode();
final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; }
//---------------public GETTERS and SETTERS - the usual Java Beans approach-----------------------// public String getCreatedByUser() { return createdByUser; }
public Date getDateCreated() { return dateCreated; }
public Date getDateUpdated() { return dateUpdated; }
public String getEntity_type() { return entity_type; }
public Long getId() { return id; }
public String getUpdatedByUser() { return updatedByUser; }
public Integer getVersion() { return version; Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale }
public void setCreatedByUser(String createdByUser) { this.createdByUser = createdByUser; }
public void setDateCreated(Date dateCreated) { this.dateCreated = dateCreated; }
public void setDateUpdated(Date dateUpdated) { this.dateUpdated = dateUpdated; }
public void setEntity_type(String entity_type) { this.entity_type = entity_type; }
public void setId(Long id) { this.id = id; }
public void setUpdatedByUser(String updatedByUser) { this.updatedByUser = updatedByUser; }
public void setVersion(Integer version) { this.version = version; }
@ManyToOne private Furnizor furnizor; Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale
@Entity public class LinieDocument extends AbstractEntity {
Double cantitate=0.0; //valoarea null trebuie evitata cu orice pret aici Double pret=0.0; //valoarea null trebuie evitata cu orice pret aici @ManyToOne BunMaterial material; @ManyToOne Document document;
}
Universitatea Alexandru Ioan Cuza din Iasi Facultatea de Economie si Administrarea Afacerilor An 3, Informatic Economic Proiectarea Sistemelor Informationale public class Receptie extends Document{ @ManyToOne DocInsotitor docInsotitor; @ManyToOne Gestiune gestiune;