Top Banner
SVEUČILIŠTE U ZAGREBU FAKULTET ORGANIZACIJE I INFORMATIKE V A R A Ţ D I N Senko Pušec ZODB ZAVRŠNI RAD Varaţdin, 2016.
28

Senko Pušec

Feb 07, 2017

Download

Documents

tranque
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Senko Pušec

SVEUČILIŠTE U ZAGREBU

FAKULTET ORGANIZACIJE I INFORMATIKE

V A R A Ţ D I N

Senko Pušec

ZODB

ZAVRŠNI RAD

Varaţdin, 2016.

Page 2: Senko Pušec

SVEUČILIŠTE U ZAGREBU

FAKULTET ORGANIZACIJE I INFORMATIKE

V A R A Ţ D I N

Senko Pušec

Matični broj: 39216/11–I

Studij: Poslovni sustavi

ZODB

ZAVRŠNI RAD

Mentor:

Doc.dr.sc Markus Schatten

Varaţdin, rujan 2016.

Page 3: Senko Pušec

1

Sadrţaj

1. Uvod..................................................................................................................................... 2

2. Objektne baze podataka ....................................................................................................... 3

2.1. Povijesni razvoj objektno-orijentiranih baza ................................................................. 3

2.2. Osnovni koncepti ........................................................................................................... 4

2.2.1. Usporedba OID-a sa Vanjskim ključem .................................................................. 5

2.3. Objektno-orijentirana baza podataka ............................................................................. 5

2.3.1. Usporedba sa relacijskim bazama podataka ............................................................ 6

2.4. Objektno-relacijske baze podataka ................................................................................ 7

3. ZODB ................................................................................................................................... 7

3.1. Svojstva ZODB-a .......................................................................................................... 7

3.2. Vrste pohrane u ZODB-u .............................................................................................. 9

3.2.1. Pickling .................................................................................................................... 9

3.2.2. Binarna stabla ......................................................................................................... 10

3.2.3. Buckets ................................................................................................................... 10

3.2.4. Pohrana kao atribut/pohrana kao BTree ................................................................. 11

3.2.5. BinaryLargeOBject ................................................................................................ 11

4. Phone Book aplikacija ........................................................................................................ 12

4.1. Dijagram klasa za bazu ................................................................................................ 13

4.1.1. Klasa User ............................................................................................................. 14

4.1.2. Klasa Contact ....................................................................................................... 14

4.1.3. Klasa PersonalContact .......................................................................................... 15

4.2. Prijava i rad u aplikaciji ............................................................................................... 15

4.2.1. Upiti prema bazi te rad s listama ............................................................................ 16

5. Zaključak ............................................................................................................................. 19

6. Literatura ............................................................................................................................. 20

7. Prilog - programski kod ...................................................................................................... 21

Page 4: Senko Pušec

2

1. Uvod

Objektno-orijentirane baze iako već dugo postoje nisu u često spominjane, barem ne

čisto objektno-orijentirane baze podataka. Iako smo se za vrijeme obrazovanja često sretali s

principima objektno-orijentirane paradigme u programskim jezicima, rješenja koja smo

razvijali često su za spremanje podataka imale obične relacijske baze podataka. Prvo

susretanje s objektno-orijentiranim bazama podataka zapravo su bili ORM alati bilo u Javi

(Persistence) ili u C# (Entity Framework) koje su nam pokazali kako je puno lakše razvijati

aplikaciju bez SQL upita već putem LINQ-a i sl.

MeĎutim ORM nije isto što i objektno-orijentirana baza podataka jer ORM zapravo

sakriva relacijsku bazu podataka i jednostavno mapira tablice u bazi podataka u klase u

programskom jeziku, dakle sve je ipak u pozadini SQL upit. Stoga smo odlučili provjeriti

čistu objektno-orijentiranu bazu podataka, a to je bio nativna python objektno-orijentirana

baza podataka ZODB, koja je nastala iz Zope Web Framework-a i koja omogućuje spremanje

čistih objekata u bazu. Postoji velik broj komercijalnih i besplatnih sustava za upravljanje

objektno-orijentiranim bazama meĎutim ZODB se čini najjednostavnijim i najlakšim za

korištenje zbog manjka dodatnog koda za integraciju u aplikacije.

Page 5: Senko Pušec

3

2. Objektne baze podataka

Objektna baza (eng. object database) ili objektno orijentirana baza podataka, je sustav

upravljanja bazama podataka u kojoj su informacije predstavljene u obliku objekata.

Relacijske baze podataka koje predstavljaju jedan od stupova informatike su jedno vrijeme

bile dostatne za tradicionalne aplikativne domene kao što je procesiranje administrativnih

podataka itd, meĎutim daljnjim razvojem informatike u mnogim drugim aplikativnim

domenama dolazi do obrade puno kompleksnijih vrsta podataka. Takvi podaci su uglavnom

bili pohranjivani u podatkovnom podsustavu operacijskog sustava ili i u specijaliziranim

podatkovnim strukturama prije nego u relacijskim bazama podataka. Jedan od primjera

kompleksnih podataka koji su se na takav ili sličan način obraĎivali je CAD/CAM sustav

(eng. computer aided modeling / design). Porastom količine podataka, mnoge značajke

DBMS sustava su postajale sve zanimljive je i štoviše potrebne. MeĎutim kako bi se te

značajke DBMS sustava mogle upotrebljavati u gore navedenim slučajevima korištenja bilo je

potrebito uvesti podršku kompleksnih tipova podataka u DBMS-ove. Koncepti objektno-

orijentiranog programiranja su uvelike utjecali na zahtjev DBMS podrške kompleksnih tipova

podataka i eventualno dovele do stvaranja objektno orijentiranih baza podataka odnosno

sustava za upravljanje objektno orijentiranim bazama podataka. Objektne baze podataka su se

kasnije razvijale u dva različita smjera: objektno-orijentirane baze te objektno-relacijske

baze.[2]

2.1. Povijesni razvoj objektno-orijentiranih baza

Objektne baze podataka ili objektno-orijentirane baze podataka su nastale na temelju

istraživanja za unutrašnju podršku strukturiranih podataka (graf) u sustavima za upravljanje

bazama podataka. Od tih istraživanja najvažnije je spomenuti sljedeća koja su imali utjecaj na

razvoj objektno orijentiranih baza: IRIS (Hewlett-Packard), ODE (Bell Labs), ORION (MCC

- Microelectronics and Computer Technology Corporation) i Zeitgeist (TI - Texas

Instruments). Rani komercijalni proizvodi su bili Gemstone (Gemstone Systems), Gbase

(Graphael) i VBase (Ontologic). Na razvoj objektno-orijentiranih baza za vrijeme 80-ih

godina 20. stoljeća veliki utjecaj je imala sve veća zainteresiranost u objektno-orijentirane

programske jezike. Objektno-orijentirani programski jezici kao što su C++ i Simula su se tek

pojavili na programskoj sceni i pružali su potpuno novi pristup razvoju software-a koji je

naglašavao korištenje objekata, te enkapsulacije objektne strukture i ponašanja korištenjem

Page 6: Senko Pušec

4

apstraktnih tipova podataka. Spajanje perzistentnih, na objektima temeljenih, podataka sa

objektno orijentiranim programskim jezicima obećavalo je novu paradigmu za baze podataka

koja bi zajedno sa fleksibilnošću objektno orijentiranih programskih jezika omogućila

efikasnu reprezentaciju velikih i kompleksnih setova podataka. Najveća prednost paradigme

objektno orijentiranih baza podataka je bilo rješenje impendance mismatch problema što joj je

omogućavalo jednostavnu integraciju baze podataka sa programskim jezikom. Impendance

mismatch problem je problem koji se odnosi na razlike izmeĎu deklarativnog pristupa bazama

podataka i relacijskog, imperativnog odnosno pristupa programskih jezika prema podacima.[]

2.2. Osnovni koncepti

Jedan od glavnih ciljeva objektnih baza podataka je podrška za perzistentnost objekata

zajedno značajkama koje se uobičajeno očekuju od sustava baza podataka, a neke od tih

značajki su: učinkovito upravljanje perzistentnim podacima, transakcije, istodobnost (eng.

concurrency) i kontrola oporavka. Izazov za objektno orijentirane baze podataka je podrška

gore navedenih značajki u kontekstu objektno orijentiranog programiranja i njegove

složenosti.

Sljedeći koncepti objektno orijentiranog programiranja su tipično vezani uz objektno

orijentirane baze podataka (Dietrich i Urban, 2014) :

složeni odnosno kompleksni objekti

identitet objekta

enkapsulacija ili učahurivanje

ekstenzibilnost

hijerarhija klasa i nasljeĎivanje

preopterećenje (overloading), polimorfizam, i late binding

Mogućnost definiranja kompleksnih objekata iz jednostavnijih je važno svojstvo

objektno-orijentiranog pristupa. Kompleksni objekti su definirani korištenje konstruktora, kao

što je npr. tuple (par) konstruktor koji kombinira jednostavne objekte kako bi napravio

kompleksniji objekt. Jedan od primjera gore navedene značajke objektno-orijentiranog

pristupa bi bio avion, za koji se može reći da je kompleksni objekt koji se sastoji od drugih

jednostavnijih objekata kao što krila, trup i motori.[1]

Page 7: Senko Pušec

5

Objekti isto tako imaju objektni identitet putem interno dodijeljenog objektnog

identifikatora (oid). Za razliku od ključeva u relacijskom modelu, oid je nepromjenjiv

(immutable) što znači da kada je objekt jednom kreiran oid ostaje isti za vrijeme trajanja

objekta. Usporedno s tim stanje objekta je promjenjivo (mutable) što znači da se vrijednosti

svojstava (eng. property) mogu mijenjati. Sustav baze podataka koristi oid kao referencu

izmeĎu objekata kako bi stvorio kompleksne objekte. Vrijednosti svojstava se stoga ne koriste

kao jedinstveni identifikatori za objekt u bazi podataka. To je zasebnost po kojoj se razlikuj

objektno-orijentirani model od modela relacijskih baza podataka, koji koristi vrijednosti

atributa (primarni - vanjski ključ).[2]

Enkapsulacija ili učahurivanje se odnosi na mogućnost stvaranja klase kao apstraktnog

tipa podatka, koji ima sučelje i implementaciju. Sučelje definira ponašanje apstraktnog tipa

podatka na konceptualnoj razini dok implementacija definira realizaciju tog ponašanja na

razini programskog jezika. Korištenjem koncepta učahurivanja, implementacija klase se može

promijeniti bez utjecanja na sučelje koje klasa pruža ostatku aplikacije. Objektno-orijentirane

baze podataka podržavaju učahurivanje specificiranjem tipova definiranih od strane korisnika.

Sa ekstenzibilnošću nema razlike izmeĎu korištenje tipova koji su definirani od strane

sustava i onih koje definira korisnik. Odnosno korisnik može stvoriti nove tipove koji

odgovaraju semantici aplikacije i koristiti ih na isti način kao i tipove koje je definirao sustav.

2.2.1. Usporedba OID-a sa Vanjskim ključem

Kao što je gore navedeno, korištenje oid kao referencu za objekt je vrlo slično

korištenju vanjskog ključa za referencu para (eng. tuple) ali s mali i bitnim razlikama. OID

može pokazivati na objekt koji je spremljen bilo gdje u bazi podataka, čak i ako se nalazi u

polju, dok je vanjski ključ ograničen na pokazivanje objekta koji se nalazi u specifično

referenciranoj relaciji. (Ramakrishnan Gehrke, 2003)

2.3. Objektno-orijentirane baze podataka

Objektno-orijentirane baze podataka ili sustavi za upravljanje objektno-orijentiranim

bazama podataka su baze podataka u kojima se spremaju objekti umjesto podataka kao što su

integeri ili string-ovi itd. Radi lakšeg pojašnjenja koncepta objektno-orijentiranih baza

podataka ukratko ćemo objasniti objekte. Objekti se prema konceptima objektno-orijentiranog

pristupa sastoje od atributa i metoda. Atributi su podaci koji odreĎuju karakteristike objekta,

Page 8: Senko Pušec

6

oni mogu biti jednostavni tipovi podataka (integer, string) ili reference na složenije tipove

podataka. Metode odreĎuju ponašanje objekta odnosno to su procedure i funkcije.

2.3.1. Usporedba sa relacijskim bazama podataka

Relacijske baze podataka spremaju podatke u tablice koje su dvodimenzionalne.

Tablice imaju stupce i retke i normalizirane su kako se podaci sadržani u njima ne ponavljaju

više nego što je potrebno. Svi stupci u tablici ovise o primarnom ključu, koji je jedinstvena

vrijednost u stupcu, radi identifikacije. Kada je specifični stupac identificiran, podaci iz

jednog ili više redaka povezanih s tim stupcem, mogu se dohvatiti ili izmijeniti. Kompleksni

tipovi podataka se ne mogu lagano spremati u relacijske baze podataka jer to zahtijeva

razbijanje kompleksnih informacija u jednostavne podatke i zahtjeva puno vremena i u većini

slučajeva puno koda. Prednosti objektno-orijentiranih baza podataka naspram relacijskih baza

podataka su:

Objekti se ne moraju sastavljati i rastavljati time štedeći vrijeme implementacije tj

kodiranja i izvršavanja

smanjeno straničenje

lakša navigacija

Bolja kontrola istodobnosti (eng. concurrency)

Model podataka se temelji na stvarnoj pojavi tj stvarnom svijetu

Dobro funkcionira za distribuirane arhitekture

Manje programskog koda ako se aplikacije pridržavaju objektno-orijentiranog

pristupa

MeĎutim u nekim slučajevima su objektno-orijentirane baze loš odabir. Ukoliko se

radi o jednostavnim aplikacijama učinkovitost te brzina pristupa su bolje u relacijskim

bazama podataka.[1] Stoga možemo reći da objektno-orijentirane baze podataka funkcioniraju

bolje sa:

CAS aplikacijama (CASE - computer aided software engineering, CAD - computer

aided design, CAM - computer aided manufacture)

Multimedijskim aplikacijama

Trgovinske aplikacije (eng. commerce)

Page 9: Senko Pušec

7

2.4. Objektno-relacijske baze podataka

Objektno-relacijske baze podataka su vrlo slične relacijskim bazama podataka ali

sadrže objektno-orijentirani model baze podataka. Objekti, klase i nasljeĎivanje su direktno

podržane i shemama baze podataka i jeziku za upite. Za objektno-relacijske baze podataka se

može reći kako predstavljaju kompromis izmeĎu objektno-orijentiranih baza i relacijskih.[1]

3. ZODB

ZODB ili Zope Object DataBase je nativna objektno-orijentirana baza podataka za

python programski jezik. jedne od glavnih značajki koje razlikuju ovu objektno-orijentiranu

bazu podataka od ostalih rješenja su: za operacije na bazi podataka se koristi python a ne neki

zasebni programski jezik, potrebne su jako male promjene koda kako bi se objekti napravili

perzistentnima, nepostojanje mapper-a za mapiranja baze podataka koji zapravo samo skriva

jedan dio baze (ORM nije isto što objektno-orijentirana baza podataka iako se na prvi pogled

možda čini tako), nije potrebna velika količina dodatnog koda za integraciju s bazom.[6]

3.1. Svojstva ZODB-a

Jedno od glavnih svojstava koje najviše privlači developere ZODB-u je njegov

minimalizam. On je zapravo ogoljena baza podataka koji sadrži samo osnovne značajke kao

što je perzistentnost i podršku za transakcije, ali to ga ne čini slabijim ili lošijim od ostalih

objektno-orijentiranih baza podataka jer, kao i većina modula u python ekosustavu, postoji

pregršt ekstenzija za nj. Jezgru samog ZODB-a čine sljedeće značajke: familijarnost - kao

što

je prethodno rečeno, ne koristi se zasebni programski jezik za operacije na bazi podataka, u

bazu se spremaju nativni python objekti te se koristi pickle modul koji je većini python

developera poznat. Budući da je cijelio ZODB pisan u pythonu moguće je vidjeti unutrašnje

mehanizme i operacije samog ZODB-a te, iako vrlo rijetko, napraviti preinake na njemu.

Jednostavnost ZODB-a je isto tako vrlo bitna značajka. Sama baza podataka je hijerarhijska

baza podataka što znači da imam korijenski object ili root koji se inicijalizira prilikom

stvaranje same baze. Taj se objekt potom koristi na isti način kao i dictionary u pythonu te

može sadržavati druge objekte koji i sami mogu biti dictionary tipa, iako postoje neke stvari

koje se trebaju uzeti u obzir i koje su kasnije opisane. Transparentnost - da bismo napravili

instancu klase perzistentnom potreban nam je modul persistence odnosno njegov dio a to je

Page 10: Senko Pušec

8

klasa Persistence. Nakon što definiramo klasu koja nasljeĎuje od klase Persistence ostali dio

spremanja objekata i ažuriranja istih, ukoliko doĎe do njihove promjene, obavlja ZODB. Isto

tako mogu se spremati objekti koji nisu perzistentni kao što je običan dictionary ili list

meĎutim tada ZODB ne može detektirati promjene na njima i potrebno ga je obavijestiti kada

doĎe do promjene. ZODB podržava ACID odnosno svoja ACID-a. Transakcijski sustavi se

brinu o stanju baze podataka odnosno sprječavaju da baza podataka doĎe u nekonzistentno

stanje, a to uspijevaju podrškom četiri svojstva koji se poznatiji pod akronimom ACID. ACID

akronim predstavlja:

Atomičnost (eng. atomicity) - ili će se sve izmjene u transakciji izvršiti odnosno zapisati na

bazu podataka ili ukoliko doĎe do pogreške i sl. cijela transakcija će biti otkazana. Upravo to

omogućuje održavanja konzistentnog stanja baze podataka u slučaju hardverske greške ili

greške pisanja.

Konzistentnost (eng. consistency) - nijedna transakcija koja zapisuje podatke na bazu neće biti

dozvoljena ukoliko bi ona ostavila bazu podataka u nekonzistentnom stanju, transakcija koja

čita odnosno dohvaća podatke će vidjeti bazu podataka u konzistentnom stanju u kojem je bila

na početku transakcije bez obzira na druge transakcije koje se istovremeno izvršavaju.

Izoliranost (eng. isolation) - prilikom izvršavanje promjena na bazi podataka od strane dva

različita programa, nijedan od njih neće moći vidjeti transakcije od drugoga dok ne izvrše

vlastite transakcije (eng. commit)

Postojanost (eng. durability) - podaci će biti spremljeni nakon što je transakcija izvršena.

Softverski ili hardverski kvar neće uzrokovati gubitak podataka nakon što je transakcija

izvršena.[7]

U slučaju velikih promjena na bazi, odnosno u slučaju izvršavanje jedne transakcije

kojim se modificira veliki broj objekata istovremeno dolazi do velikog zauzeća memorije koje

usporava rad, a razlog usporavanje leži u načinu funkcioniranja ZODB-a, naime sve promjene

koje se izvršavaju u jednoj transakciji se zadržavaju u memoriji dok se transakcija ne izvrši.

Kako bi se poboljšale performanse i smanjilo zauzeće memorije, ZODB sadrži svojstvo Save

Point koje omogućuje developeru da izvrši jedan dio transakcije prije nego je transakcija

gotova kako bi se zapisale promjene na bazi podataka i oslobodio dio memorije koju je

transakcija zauzela. Ukoliko transakcija koja je izvršena nije trebala biti izvršena ili se

greškom prebrisao dio baze ZODB sadrži jednostavan mehanizam za vraćanje baze u

prethodno stanje odnosno za poništavanje (eng. roll back) transakcije koji se zove Undo. Ova

značajka postoji i funkcionira zato što ZODB pamti stanje baze podataka prije i poslije svake

transakcije. Upravo to omogućava poništavanje bilo koje transakcije tj. promjena koje je

Page 11: Senko Pušec

9

transakcija izvela na bazi. Treba uzeti u obzir kako je ovo jako jednostavan mehanizam te

ukoliko je objekt nakon transakcije koju želimo poništiti ponovno promijenjen od strane

druge transakcije, poništavanje neće biti moguće zbog gore navedenih ACID svojstava

odnosno zbog konzistentnosti. S obzirom da, kao što je gore navedeno, ZODB pamti svaku

promjenu na bazi odnosno pamti svaku transakciju, moguće je vidjeti stanje objekta prije i

poslije promjene (transakcije) te ih usporediti što omogućuje implementaciju jednostavnog

verzioniranja. ZODB dakle može spremati skoro pa sve vrste objekata koji postoje u python

programskom jeziku, meĎutim ukoliko želimo spremiti posebne vrste podataka kao što su

multimedijski podaci itd. potrebno je koristiti ZODB-ov Blob. Veliki binarni objekti kao što

su slike i dokumenti bi se trebali spremati u Blob objekt jer bi njihovo spremanje kao običnih

svojstava objekta znatno usporilo bazu podataka te značajno povećalo veličinu baze. ZODB

potom ima specijalnu vrstu Storage-a koje se zove Blob Storage pomoću kojeg je moguće

lako rukovati velikim datotekama bez degradiranja performansi. [7]

3.2. Vrste pohrane u ZODB-u

Vrste različitih pohrana (storing backends) u ZODB:

Pickling

Binarna stabla (engl. Binarytrees)

Buckets

Pohrana kao atribut / pohrana kao BTree

BLOBs (BinaryLargeOBject)

U nastavku je objašnjeno kako se podatci spremaju u ZODB što je bitno za razumijevanje

ponašanja i optimizacije Plone baze podataka.

3.2.1. Pickling

ZODB je objektno orijentirana baza podataka. Svi podaci u ZODB se pohranjuju kao pickled

Python objekti. Pickle je objektni serializacijski modul Python standardne biblioteke.

Page 12: Senko Pušec

10

Svaki put kada je objekt pročitan i nije spremljen u meĎuspremnik (not cached),

objekt se čita iz ZODB baze podataka i unpickled je

Svaki put kad je objekt napisan, on je pickled i pomoću sustava za transakcije ga se

dodaje na ZODB bazu podataka

Pickle format je niz bajtova. Na donjem primjeru možemo vidjeti kako to izgleda:

>>> import pickle

>>>data = { "key" : "value" }

>>>pickled = pickle.dumps(data)

>>>printpickled

(dp0

S'key'

p1

S'value'

p2

s.

To nije format optimalan za čitanje. Čak i ako koristimo SQL temeljeni RealStorage ZODB

backend, objekti su i dalje pickled na bazu podataka, SQL ne podržava varirajući tabličnu

shemu po redu i Python objekti nemaju fiksnu shmenu za format.

3.2.2. Binarna stabla

Podatci su obično organizirani u binarna stabla ili Bstabla (engl. BTrees). Točnije, podatci se

obično pohranjuju kao objektno orijentirana binarna stabla (engl. OOBtree) koja daju Python

objekt kao ključ i Python object value mapping. Ključ je objekt id u parentcontainer-u kao

string, a vrijednost je bilo koji pickleable Python objekt ili primitive koju spremamo u bazu

podataka.[7]

3.2.3. Buckets

BTree pophranjuje podatke u buckets (OOBucket).

Bucket je najmanja jedinica podataka koje se jednom upisuje u bazu podataka. Buckets se

pune sporo: BTree učitava jedino bucket-e koji spremaju vrijednosti ključeva kojima se

pristupa. BTree pokušava staviti što je više moguće podataka u jedan bucket. Kada se jedna

vrijednost u bucket-u promijeni, cijeli bucket mora biti prepisan na disk. Zadana veličina

bucket-a je 30 objekata.[7]

Page 13: Senko Pušec

11

3.2.4. Pohrana kao atribut / pohrana kao BTree

Plone ima dva temeljna načina za pohranu podataka:

Attributestorage (pohranjuje vrijednosti direktno u pickled objekte)

Annotationstorage (OOBTreebased) – plone objekti imaju atribut _annotations_ koji je

OOBTree za pohranu objekata bez konflikta prilikom imenovanja.

Kada su objekti pohranjeni u annotation storage obliku, čitanje vrijednosti objekta zahtjeva

barem jedan dodatni upit u bazi podataka, za učitavanje prvog bucket-a iz OOBTree.

Ako će se vrijednost koristiti često, posebice ako se čita kod pregleda sadržaja objekta,

pohranjivanje u attribute je efikasnije nego pohranjivanje u annotation. Razlog tome je što

BTree _annotation_ je zasebni perzistentni objekt koji mora biti učitan u memoriju i može

izbaciti nešto drugo iz ZODB meĎuspremnika (engl .ZODB cache).

Ako je u atributu pohranjena velika vrijednost povećati će se upotreba memorije, jer će se

učitati u memoriju svaki put kada je objekt dohvaćen iz ZODB.[7]

3.2.5. BinaryLargeOBject

BLOBs su veliki binarni objekti poput datoteka ili slika. Podržani su od verzije ZODB 3.8.x.

Kada koristimo BLOB sučelje za pohranu i dohvaćanje podataka, oni su spremljeni fizički

kao datoteke na datotečnom sustavu. Datotečni sustav, kao što mu ime govori, je osmišljen

kako bi upravljao podatcima i ima daleko bolje performanse sa velikim binarnim podatcima

nego pohranjivanje podataka u ZODB.

BLOBs su streamable što znači da možemo početi posluživati datoteku od početka te datoteke

na HTTP, bez potrebe da se učita cijela datoteka u memoriju(što je sporo).[7]

Page 14: Senko Pušec

12

4. Phone Book aplikacija

Primjer korištenja ZODB-a u napravljen za ovaj završni rad je jednostavna aplikacija

za upravljanje kontaktima. Korišteni su sljedeći moduli:

tkinter - GUI za aplikaciju

flask_bcrypt - za enkripciju lozinke

ZODB - objektno-orijentirana baza podataka

persistent - generička implementacija perzistentnosti

ZEO - Zope Enterprise Objects, client-server sistem za dijeljenje spremnika (storage)

izmeĎu više klijenata

transaction - generička implementacija transakcija

logging - standardni mehanizam za logiranje

Jedan od uvjeta postavljenih za ovu temu je bilo korištenje Client Storage-a odosno mrežnog

spremnika i zbog toga je korišten modul ZEO koji unutar sebe sadrži runzeo.py skriptu koja

pokreće mrežnog poslužitelja na kojem se nalazi spremnik i koji se potom dijeli izmeĎu

korisnika

Page 15: Senko Pušec

13

4.1. Dijagram klasa za bazu

Class diagram koji je korišten u ovoj aplikaciji je vrlo jednostavan i sastoji se od klase user

,contact te personalContact. Klasa user sadrži svojstva (eng. property) username ili korisničko

ime, password ili lozinka te PersistenList object Contacts u koji se spremaju kontakti nakon

što se pozove funkcija add_contacts().

1. Dijagram klasa koje se spremaju u bazu

Sve klase koje se nalaze na dijagramu nasljeĎuju klasu Persistent koja je

implementacija generičke perzistencije za python. Korisnik može imati više osobnih

kontakata ali kontakt može imati samo jednog autora koji je objekt tipa User.

Budući da sve klase nasljeĎuju baznu klasu Persistent, ZODB može detektirati promjene na

njima i tako obavijestiti ostale klijente koje koriste istu bazu odnosno Client Storage.

Page 16: Senko Pušec

14

4.1.1. Klasa user

Klasa user kao što je vidljivo na dijagramu klasa ima svojstva USERNAME,

PASSWORD te CONTACTS.

2. Isječak koda koji prikazuje implementaciju klase user

Prilikom instanciranja klase odnosno poziva se ugraĎena python funkcija __init__()

odnosno u slučaju pythona konstruktor. On prihvaća dva argumenta, username i password,

password se pomoću modula flask_bcrypt hashira radi sigurnosti jer spremanje lozinki u bazi

u obliku čistog texta je primjer loše prakse.

Drugi zanimljivi dio je dodavanje kontakata pomoću funkcije add_contact() koja instancira

klasu PersonalContact koja nasljeĎuje klasu Contact. Kao parametre prima name,

contact_number te nasljeĎivanjem i parametar autor gdje se stavlja trenutni user koji

instancira klasu.

4.1.2. Klasa Contact

Sadrži svojstva:

NAME - ime kontakta koji se dodaje

CONTACT-NUMBER - telefonski broj/mobitel kontakta

AUTOR - korisnik koji je kreirao ovaj kontakt

Page 17: Senko Pušec

15

3. Isječak koda koji prikazuje implementaciju klase Contact

4.1.3. Klasa PersonalContact

NasljeĎuje sva svoja svojstva od klase Contact.

4. Isječak koda koji prikazuje implementaciju klase PersonalContact

4.2. Prijava i rad u aplikaciji

Aplikacija je vrlo jednostavna i sastoji se od samo dva prozora, prvi prozor je logiranje

korisnika odnosno provjera njegovih podataka u bazi podataka.

5. Prozor za prijavu korisnika

Nakon uspješne prijave korisnika, korisniku se otvara prozor tk2 koji predstavlja sve njegove

kontakte i omogućuje mu CRUD nad svojim kontaktima. Korisnik vidi isključivo kontakte

Page 18: Senko Pušec

16

koje je on kreirao i ne druge. Dakle nakon uspješne prijava na sustav se pokreće upit koji

dohvaća sve kontakte kojima je svojstvo autor jednako autoru koji se uspješno prijavio na

sustav.

6. Prozor za rad sa kontaktima

U listbox-u se nalaze svi kontakti koje je napravio trenutno prijavljeni korisnik. Klikom na

neki od njih te pritiskom na tipku Load dohvaćaju se dodatni podaci o trenutno odabranom

kontaktu i ispisuju se u textbox-ove. Za brisanje kontakta opet se mora odabrati jedan kontakt

iz listobox te potom pritisnuti dugme Delete. Za dodavanje novog korisnika potrebno je

upisati podatke u polja name i phone te potom pritisnuti na tipku Add. Za ažuriranje korisnika

potrebno je odabrati korisnika iz listbox-a te potom obaviti promjene u poljima name i/ili

phone te stisnuti dugme Update.

4.2.1. Upiti prema bazi te rad s listama

Većina upita dolje opisana i prikazana su vrlo jednostavna i zahvaljujući

jednostavnosti pythona vrlo kratka. Slični upiti u drugim programskim jezicima bi vjerojatno

zahtijevali veći broj iteracija i veću količinu koda.

Inicijalizacija baze te dodavanje početnih korisnika se izvršava u tri koraka, prvi korak je

samo spajanje na ClientStorage, otvaranje konekcije te dohvaćanje root elementa iz baze.

Potom se dodaju dva nova ključa 'korisnici' te 'kontakti' koji su tipa persistent.PersistentList().

Nakon dodavanja ključeva dolazi do instanciranje user klase gdje dodajemo par probnih

korisnika te probnih kontakata koji se potom dodaju u bazu.

Page 19: Senko Pušec

17

7. Isječak koda koji prikazuje implementaciju klase database

8. Isječak koda koji prikazuje dodavanje početnih korisnika i njihovih kontakata

9. Isječak koda koji prikazuje pražnjenje tablice sa korisnicima

Nakon početnog postavljanja baze podataka, možemo početi raditi sa Persistent listama kod

kojih su operacije u načelu jednake kao i operacije kod običnih python listi. Dakle elementi u

listi imaju svoje indekse i sl

Dohvaćanje korisnika putem aplikacije funkcionira na sljedeći način:

Page 20: Senko Pušec

18

[x for x in root['kontakti'] if x.AUTOR.NAME == 'senko']

Nakon što je upit izvršen vraća nam se lista koja ovisno o tome postoji li kontakt kojeg je

kreirao autor 'senko' vraća jedan ili više elemenata, ako ne postoje kontakti koje je kreirao

korisnik 'senko' vraća se prazna lista. Ukoliko želimo dobiti samo jednog korisnika iz liste

tada koristimo standardnu python sintaksu za liste:

[x for x in root['kontakti'] if x.AUTOR.NAME == 'senko'][0]

Iako je ovo tehnički loš primjer s obzirom da će dohvatiti prvog korisnika u listi, meĎutim

možemo još dodatno sačuvati gornju listu u varijabli i onda po njoj na isti način izvršiti upit:

[x for x in rezultat if x.NAME == 'marko'][0]

S ovim upitom vraća nam se prvi element u listi koji odgovara argumentima postavljenima u

upitu. Dakle glavni predložak za rad s listama izgleda sljedeće:

[predlozak for varijabla in lista if uvjet]

I uvijek kao rezultat vraća objekt tipa lista, osim ako ne odredimo indeksu koji želimo vratiti

kao što je opisano gore.[8] Možemo se još dodatno poigrati s listama i tražiti vraćanje

isključivo imena kontakata koji odgovoraju upitu:

[x.NAME for x in root['kontakti'] if x.AUTOR.NAME == 'senko']

Dakle u ovom slučaju upit vraća listu korisničkih imena koji su povezani s autorom 'senko'.

Brisanje podataka iz liste je isto tako jednostavno:

del root['kontakti'][index_zapisa]

del root['kontakti'][:]

del root['kontakti'][:3]

Prva operacija briše specifični indeks u listi, druga operacija briše sve elemente u listi, treća

operacije briše sve elemente u listi do elementa s indeksom tri.[8]

Page 21: Senko Pušec

19

5. Zaključak

Zope Object DataBase je jedan od boljih primjera jednostavnosti i lakoće rada s objektnim-

bazama. Pisana u potpunosti u python programskom jeziku, omogućuje lako korištenje svih

python operacija nad objektima i time omogućuje lako izvršavanje programske logike te brzo

dohvaćanje podataka iz baze. MeĎutim za osobe koje dolaze bez predznanja o opčenitom

načinu rada s objektnim bazama može predstavljati poneke poteškoće, usprkos tome možemo

reći kako je rad sa ZODB-om te njegova integracija bilo u desktop aplikacije ili web

aplikacije vrlo jednostavna.

ZODB je vrlo jednostavna i zbog toga moćna objektno-orijentirana baza podataka ali

kao i svaka objektno-orijentirana baza podataka i on je namijenjen za posebne aplikativne

domene iako se može koristi u svakoj domeni. ZODB je prvenstveno namijenjen aplikacijama

u kojima nema velikog broja upisivanja u bazu, ali ima dohvaćanja podataka te pretraga po

bazi. Najbolji primjeri korištenja ZODB-a su wiki sustav izgraĎen uz pomoć Pyramid python-

a, te zodb-browser, koji su sve aplikacije u kojima nema velikog broj upisa. Stoga možemo

reći kako je ZODB, iako jako jednostavan i zbog toga privlačan, namijenjen za veće

aplikacije, distribuirane aplikacije dok je za jednostavne aplikacije kao što je gore opisana

aplikacija jednostavno neiskorišten do svog punog potencijala. Najbolji primjeri prave

aplikativne primjene ZODB-a se nalaze u plone CMS-u te Zope Web Frameworku gdje

ZODB dolazi do svog punog potencijala.

Page 22: Senko Pušec

20

6. Literatura

[1] Suzanne W D,Susan D U (2011) Fundamentals of Object Databases:Object-Oriented

and Object-Relational Design San Rafael: Morgan & Claypool.

[2] Ramakrishnan R, Gehrke J (2001) Database Management Systems (3. izd.). Boston:

McGraw-Hill.

[3] Weitershausen P (2009) Web Component Development with Zope (3. izd.). Boston:

Springer.

[4] Grinberg M (2013) Flask Web Development (2. izd.). Sebastopol: O'Reilly.

[5] Chaudhary B (2015)Tkinter GUI Application Development Blueprints(1. izd.).

Birmingham: Packt Publishing.

[6] Lott F S (2015) Mastering Object-oriented Python (2. izd.). Birmingham: Packt

Publishing.

[7] ZODB - Native object database for python - Documentation. (2016). Preuzeto 2. rujna

2016. s http://www.zodb.org/en/latest/

[8] Schatten Markus, Objektno-orijentirane baze podataka prezentacija (2015)

Page 23: Senko Pušec

21

7. Prilog - programski kod

import transaction

from ZEO import ClientStorage

from ZODB import DB

import tkinter

from flask_bcrypt import check_password_hash

ADDRESS = (('188.166.166.155', 8090))

ZODB = {}

TRENUTNI = None

def create_connection():

global root

ZODB['storage'] = ClientStorage.ClientStorage(ADDRESS)

ZODB['db'] = DB(ZODB['storage'])

ZODB['connection'] = ZODB['db'].open()

root = ZODB['connection'].root()

def close_connection():

ZODB['connection'].close()

ZODB['db'].close()

ZODB['storage'].close()

def commit(thing):

try:

root['korisnici'].extend(thing)

transaction.commit()

except Exception as ex:

print ex.message

def get_korisnic():

print root['korisnici']

def callback(user,passw):

global tr

try:

a = [x for x in root['korisnici']

if x.USERNAME == user][0]

if check_password_hash(a.PASSWORD, passw):

tr = a

create_gui2()

else:

return None

except IndexError:

return None

Page 24: Senko Pušec

22

###########################################################################

############################################

#### LOGIN

###########################################################################

############################################

def create_gui():

window = tkinter.Tk()

window.title("Python Games Login")

window.geometry("270x210")

window.configure(bg="#39d972")

title1 = tkinter.Label(window, text="--Log in to play the Python Games--\n",

bg="#39d972")

usertitle = tkinter.Label(window, text="---Username---", bg="#39d972")

passtitle = tkinter.Label(window, text="---Password---", bg="#39d972") message = tkinter.Label(window, bg="#39d972")

user = tkinter.Entry(window)

passw = tkinter.Entry(window, show='*')

go = tkinter.Button(window, text="Log in!",command=lambda:

callback(user.get(),passw.get()), bg="#93ff00")

title1.pack()

usertitle.pack()

user.pack()

passtitle.pack()

passw.pack()

go.pack()

message.pack()

window.mainloop()

###########################################################################

############################################

#### LOGIN

###########################################################################

############################################

###########################################################################

############################################

#### PBOOK

###########################################################################

############################################

def whichSelected () :

print "At %s of %d" % (select.curselection(), len(phonelist))

return int(select.curselection()[0])

Page 25: Senko Pušec

23

def addEntry () :

s = tr.add_contact(name.get(),phone.get())

root['kontakti'].extend([s])

transaction.commit()

setSelect()

def updateEntry() :

rez = [x for x in root['kontakti'] if x.NAME == select.get(select.curselection()[0])][0]

ind = root['kontakti'].index(rez)

rez.NAME = name.get()

rez.CONTACT_NUMBER = phone.get()

root['kontakti'][ind] = rez

transaction.commit()

setSelect()

def deleteEntry() :

rm = [x for x in root['kontakti'] if x.NAME == select.get(select.curselection()) ][0]

print rm.NAME

root['kontakti'].remove(rm)

transaction.commit()

setSelect()

def loadEntry () :

name.delete(0, tkinter.END)

phone.delete(0,tkinter.END)

rez = [x for x in root['kontakti'] if x.NAME == select.get(select.curselection()[0])][0]

name.insert(tkinter.END,rez.NAME)

phone.insert(tkinter.END, rez.CONTACT_NUMBER)

def makeWindow () :

global name,nameVar,phoneVar, select, phone

win = tkinter.Tk()

nameVar = tkinter.StringVar()

frame1 = tkinter.Frame(win)

frame1.pack()

phoneVar = tkinter.StringVar()

tkinter.Label(frame1, text="Name").grid(row=0, column=0, sticky=tkinter.W)

name = tkinter.Entry(frame1, textvariable=nameVar)

name.grid(row=0, column=1, sticky=tkinter.W)

tkinter.Label(frame1, text="Phone").grid(row=1, column=0, sticky=tkinter.W)

phone= tkinter.Entry(frame1, textvariable=phoneVar)

phone.grid(row=1, column=1, sticky=tkinter.W)

frame2 = tkinter.Frame(win) # Row of buttons

frame2.pack()

Page 26: Senko Pušec

24

b1 = tkinter.Button(frame2, text=" Add ", command=addEntry)

b2 = tkinter.Button(frame2, text="Update", command=updateEntry)

b3 = tkinter.Button(frame2, text="Delete", command=deleteEntry)

b4 = tkinter.Button(frame2, text=" Load ", command=loadEntry)

b1.pack(side=tkinter.LEFT); b2.pack(side=tkinter.LEFT)

b3.pack(side=tkinter.LEFT); b4.pack(side=tkinter.LEFT)

frame3 = tkinter.Frame(win) # select of names

frame3.pack()

scroll = tkinter.Scrollbar(frame3, orient=tkinter.VERTICAL)

select = tkinter.Listbox(frame3, yscrollcommand=scroll.set, height=6)

scroll.config (command=select.yview)

scroll.pack(side=tkinter.RIGHT, fill=tkinter.Y)

select.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=1)

return win

def setSelect () :

select.delete(0,tkinter.END)

rezultat = [x for x in root['kontakti'] if x.AUTOR.USERNAME == tr.USERNAME]

print rezultat

for x in rezultat:

select.insert(tkinter.END, x.NAME)

def create_gui2():

win = makeWindow()

setSelect ()

win.mainloop()

if __name__ == '__main__':

create_connection()

create_gui()

from persistent import Persistent

from flask_bcrypt import generate_password_hash

from persistent.list import PersistentList

class user(Persistent):

USERNAME = None

PASSWORD = None

CONTACTS = []

def __init__(self,username,password):

self.USERNAME = username

self.PASSWORD = generate_password_hash(password=password)

self.CONTACTS = PersistentList()

Page 27: Senko Pušec

25

def add_contact(self,name,contact_number):

contact = PersonalContact(name=name,contact_number=contact_number,autor=self)

self.CONTACTS.append(contact)

return contact

class Contact(Persistent):

NAME = None

CONTACT_NUMBER = None

AUTOR = None

def __init__(self,name, contact_number,autor):

self.NAME = name

self.CONTACT_NUMBER = contact_number

self.AUTOR = autor

class PersonalContact(Contact):

def __init__(self,name,contact_number,autor):

Contact.__init__(self,name,contact_number,autor)

class Singleton:

"""

A non-thread-safe helper class to ease implementing singletons.

This should be used as a decorator -- not a metaclass -- to the

class that should be a singleton.

The decorated class can define one `__init__` function that

takes only the `self` argument. Other than that, there are

no restrictions that apply to the decorated class.

To get the singleton instance, use the `Instance` method. Trying

to use `__call__` will result in a `TypeError` being raised.

Limitations: The decorated class cannot be inherited from.

"""

def __init__(self, decorated):

self._decorated = decorated

def Instance(self):

"""

Returns the singleton instance. Upon its first call, it creates a

new instance of the decorated class and calls its `__init__` method.

On all subsequent calls, the already created instance is returned.

"""

try:

return self._instance

Page 28: Senko Pušec

26

except AttributeError:

self._instance = self._decorated()

return self._instance

def __call__(self):

raise TypeError('Singletons must be accessed through `Instance()`.')

def __instancecheck__(self, inst):

return isinstance(inst, self._decorated)