Πανεπιστήμιο Πειραιώς Σχολή Τεχνολογιών Πληροφορικής και Επικοινωνιών Τμήμα Ψηφιακών Συστημάτων 7. Ανάπτυξη Εφαρµογών Βάσεων Δεδοµένων Σχεδιασμός Βάσεων Δεδομένων Χρήστος Δουλκερίδης 2017-18
Διάρθρωση Διάλεξης Ανάπτυξη εφαρμογών βάσεων δεδομένων Χρήση της SQL στον κώδικα εφαρμογών Ενσωματωμένη (embedded) SQL για ιστορικούς κυρίως λόγους Λογικοί δρομείς (cursors) Java Data Base Coectivity (JDBC) Δουλκερίδης 2
Χρήση της SQL στον Κώδικα Εφαρµογών (1/2) Οι εντολές SQL μπορούν να κληθούν από μια γλώσσα προγραμματισμού, η οποία καλείται γλώσσα υποδοχής (host laguage), π.χ. C++, Java, Οι εντολές SQL μπορούν να αναφέρονται σε μεταβλητές της γλώσσας προγραμματισμού (host variables) Περιλαμβάνονται και ειδικές μεταβλητές που χρησιμοποιούνται για να επιστρέφουν την τρέχουσα κατάσταση (status) Πρέπει να προηγείται μια εντολή που αποκαθιστά τη σύνδεση με την κατάλληλη βάση δεδομένων Υπάρχουν δύο κύριες προσεγγίσεις Ενσωμάτωση της SQL στις γλώσσες προγραμματισμού (Embedded SQL) Δημιουργία ειδικού API (Applicatio Programmig Iterface) για την κλήση των SQL εντολών (JDBC, ODBC) Δουλκερίδης 3
Χρήση της SQL στον Κώδικα Εφαρµογών (2/2) Επιπλοκές του συνδυασμού Οι σχέσεις στην SQL είναι πολυσύνολα εγγραφών Δεν υπάρχει εκ των προτέρων όριο στο πλήθος των εγγραφών Παραδοσιακά στις διαδικαστικές γλώσσες προγραμματισμού (π.χ. C) δεν υπήρχε τέτοια δομή Η SQL υποστηρίζει το μηχανισμό των λογικών δρομέων (cursors) για να το αντιμετωπίσει Δουλκερίδης 4
Ενσωµατωµένη SQL Ένας προεπεξεργαστής (pre-processor) μετατρέπει τις SQL εντολές σε ειδικές κλήσεις ενός API Στη συνέχεια χρησιμοποιείται ένας τυπικός μεταγλωττιστής για τη μεταγλώττιση του κώδικα Δομές της γλώσσας Σύνδεση με μια ΒΔ EXEC SQL CONNECT Δήλωση μεταβλητών EXEC SQL BEGIN [END] DECLARE SECTION Εντολές EXEC SQL statemet; Δουλκερίδης 5
Ενσωµατωµένη SQL: Μεταβλητές Υπάρχουν επιπλέον δύο ειδικές μεταβλητές για το χειρισμό σφαλμάτων SQLCODE Τύπου log Παίρνει αρνητική τιμή αν προκύψει κάποιο σφάλμα SQLSTATE Τύπου char[6] Επιστρέφει προκαθορισμένους κωδικούς για τα συνηθέστερα σφάλματα EXEC SQL BEGIN DECLARE SECTION char ame[20]; log sid; short ratig; float age; EXEC SQL END DECLARE SECTION Δουλκερίδης 6
Λογικοί Δροµείς (Cursors) Μπορούμε να δηλώσουμε ένα δρομέα σε μια σχέση ή σε μια επερώτηση (uery) Μπορούμε να Ανοίξουμε (ope) ένα δρομέα Ανακτήσουμε (fetch) μια πλειάδα επαναλαμβανόμενα Μετακινήσουμε (move) το δρομέα, μέχρι να ανακτηθούν όλες οι πλειάδες Για να ελέγχουμε τη σειρά με την οποία επιστρέφονται οι πλειάδες, που προσπελάζονται από ένα δρομέα, χρησιμοποιείται το ORDER BY Μπορούμε να τροποποιήσουμε ή να διαγράψουμε μια πλειάδα την οποία προσπελαύνει ένας δρομέας Δουλκερίδης 7
Παράδειγµα Ένας δρομέας που επιστρέφει με αλφαβητική σειρά τα ονόματα των ναυτικών που έχουν κάνει κράτηση σε κόκκινη βάρκα DECLARE sifo CURSOR FOR SELECT S.same FROM Sailors S, Reserves R, Boats B WHERE S.sid=R.sid AND R.bib=B.bid AND B.color= red ORDER BY S.same OPEN sifo FETCH sifo INTO :same CLOSE sifo Δουλκερίδης 8
Παράδειγµα: Ενσωµατωµένη SQL σε C #iclude <stdio.h> EXEC SQL INCLUDE SQLCA; EXEC SQL BEGIN DECLARE SECTION; /* declare host variables */ char db_ame[8]; /* database ame */ char video_title[30]; /* title of the video */ short video_id; /* serial umber */ char director[20]; /* director ame */ EXEC SQL END DECLARE SECTION; void mai() { strcpy(db_ame, MyDB"); EXEC SQL CONNECT TO :db_ame; if (slca.slcode!= 0) { pritf("coect failed!: reaso %ld\", slca.slcode); exit(1); }. Δουλκερίδης 9
Παράδειγµα: Ενσωµατωµένη SQL σε C EXEC SQL DECLARE c1 CURSOR FOR SELECT video_title FROM video; EXEC SQL OPEN c1; do { EXEC SQL FETCH c1 ito :video_title; if (SQLCODE!= 0) break; pritf("%s\",video_title); } while (1); } EXEC SQL CLOSE c1; EXEC SQL CONNECT RESET; Δουλκερίδης 10
Δυναµική SQL Όταν τα αλφαριθμητικά των SQL επερωτήσεων είναι γνωστά στο rutime Δίνεται η δυνατότητα κατασκευής των SQL εντολών στο rutime Παράδειγμα: όταν κάποιες τιμές συμπληρώνονται από φόρμες char strig[100]; /* buffer for SQL uery */ strcpy(strig, "SELECT video_title FROM video"); /* Prepare the uery i the ru time */ EXEC SQL PREPARE Q1 FROM :strig; EXEC SQL DECLARE c1 CURSOR FOR Q1; Δουλκερίδης 11
JDBC
Database APIs Αντί να τροποποιούμε το μεταγλωττιστή, προσθέτουμε μια βιβλιοθήκη με κλήσεις (database calls) προς τη ΒΔ (API) Η διεπαφή είναι προτυποποιημένη (διαδικασίες και αντικείμενα) Μεταφέρονται strigs από τη γλώσσα προγραμματισμού Τα σύνολα που προκύπτουν ως αποτέλεσμα, παρουσιάζονται με ένα φιλικό ως προς τη γλώσσα τρόπο Το JDBC της Su (Java API) Το ODBC της Microsoft (API για κλήσεις Visual Basic, C#, κτλ) Χρησιμοποιείται ένας driver που χειρίζεται τις κλήσεις και τις μεταφράζει σε κώδικα προσανατολισμένο για DBMS To DBMS και η εφαρμογή πρέπει να είναι συμβατές ως προς το JDBC ή το ODBC Δουλκερίδης 13
Αρχιτεκτονική Γλώσσα Προγραμματισμού Η εφαρμογή Αρχικοποιεί και τερματίζει τις συνδέσεις Υποβάλλει SQL εντολές Ο driver maager Φορτώνει τον driver Ο driver Συνδέεται με τη ΒΔ Μεταδίδει τις αιτήσεις (ueries) Επιστρέφει τα αποτελέσματα Η βάση δεδομένων ΕΦΑΡΜΟΓΗ Driver Maager DRIVER Επεξεργάζεται τις SQL εντολές ΒΔ Δουλκερίδης 14
JDBC Αρχιτεκτονική (1/2) Τέσσερις τύποι drivers Type 1 Γέφυρα (bridge) Μεταφράζει τις SQL εντολές σε o-ative API Π.χ. Γέφυρα JDBC-ODBC Σε κάθε cliet πρέπει να υπάρχει κώδικας για ODBC και JDBC driver Type 2 Απευθείας μετάφραση σε ative API, o-java driver Μεταφράζει τις SQL εντολές σε ative API της πηγής δεδομένων Σε κάθε cliet πρέπει να υπάρχει συγκεκριμένος για το λειτουργικό σύστημα δυαδικός κώδικας Δουλκερίδης 15
JDBC Αρχιτεκτονική (2/2) Type 3 Δικτυακή γέφυρα (etwork bridge) Στέλνει τις εντολές μέσω του δικτύου σε έναν middleware server που επικοινωνεί με την πηγή δεδομένων Σε κάθε cliet απαιτείται μόνο ένας «μικρός» JDBC driver Type 4 Απευθείας μετάφραση σε ative API μέσω Java driver Μετατρέπει τις JDBC κλήσεις απευθείας στο πρωτόκολλο δικτύου που χρησιμοποιεί το DBMS Σε κάθε cliet απαιτείται συγκεκριμένος για το DBMS Java driver Δουλκερίδης 16
JDBC Κλάσεις και Διεπαφές Για να υποβάλλουμε μια SQL ερώτηση απαιτούνται τα εξής βήματα Φορτώνεται ο driver Συνδεόμαστε με την πηγή δεδομένων Εκτελούμε τις SQL εντολές Δουλκερίδης 17
JDBC Driver Maagemet Όλοι οι drivers διαχειρίζονται από την κλάση DriverMaager Φόρτωση ενός JDBC driver Μέσα στον κώδικα Java: class.forname( com.microsoft.jdbc.slserver.sqlserverdriver ); Δουλκερίδης 18
Συνδέσεις (Coectios) στο JDBC Αλληλεπίδραση με μια πηγή δεδομένων μέσω sessios Κάθε σύνδεση προσδιορίζει ένα λογικό sessio JDBC URL: jdbc:<subprotocol>:<otherparameters> Παράδειγμα: Strig url = jdbc:microsoft:slserver://labsql;databasename=northwid; User=OMADA20;Password=PWD20; Coectio co; try { co = DriverMaager.getCoectio(url); } catch (SQLExceptio ex) { } Δουλκερίδης 19
Class Coectio public it gettrasactioisolatio() void settrasactioisolatio(it level) Ορίζει το επίπεδο απομόνωσης για την τρέχουσα σύνδεση public boolea getreadoly() void setreadoly(boolea b) Καθορίζει εάν οι εντολές στην τρέχουσα σύνδεση είναι μόνο για ανάγνωση public boolea getautocommit() void setautocommit(boolea b) Εάν είναι true, κάθε εντολή θεωρείται ξεχωριστή συναλλαγή και καταχωρείται στη βάση. Διαφορετικά, πρέπει ο προγραμματιστής να ορίσει πότε η συναλλαγή θα καταχωρηθεί στη βάση (commit) ή εάν θα ανακληθεί (rollback) public boolea isclosed() Ελέγχει εάν η σύνδεση είναι ακόμη ανοικτή Δουλκερίδης 20
Εκτέλεση SQL Εντολών (1/2) Τρεις διαφορετικοί τρόποι εκτέλεσης SQL εντολών Statemet (για στατικές και δυναμικές SQL εντολές) PreparedStatemet (ημιστατικές εντολές SQL) CallableStatemet (αποθηκευμένες διαδικασίες) Κλάση PreparedStatemet Precompiled, παραμετρικές εντολές SQL Η δομή είναι προκαθορισμένη Όμως οι τιμές των παραμέτρων μπορούν να καθοριστούν κατά τη διάρκεια εκτέλεσης (rutime) Δουλκερίδης 21
Εκτέλεση SQL Εντολών (2/2) Strig sl = INSERT INTO Sailors VALUES (?,?,?,?) ; PreparedStatemet pstmt = co.preparestatemet(sl); pstmt.clearparameters(); pstmt.setit(1,sid); pstmt.setstrig(2,same); pstmt.setit(3,ratig); pstmt.setfloat(4,age); // Γνωρίζουμε ότι δεν επιστρέφει πλειάδες, οπότε χρησιμοποιούμε // την executeupdate() it umrows = pstmt.executeupdate(); Δουλκερίδης 22
ResultSets (1/2) Το PreparedStatemet.executeUpdate() επιστρέφει μόνο το πλήθος των εγγραφών που επηρεάστηκαν Το PreparedStatemet.executeQuery() επιστρέφει δεδομένα, ενθυλακωμένα σε ένα αντικείμενο ResultSet (κάτι σαν δρομέας) Παράδειγμα: ResultSet rs = pstmt.executequery(sl); // rs is ow a cursor while (rs.ext()) { // process the data } Δουλκερίδης 23
ResultSets (2/2) Διαθέσιμες μέθοδοι ενός ResultSet previous() Μετακινεί το δρομέα μια πλειάδα πίσω absolute(it um) Μετακινεί το δρομέα στην πλειάδα με αριθμό um relative(it um) Μετακινεί το δρομέα um πλειάδες από την τρέχουσα first() last() Δουλκερίδης 24
Πρόσβαση στα Μεταδεδοµένα της ΒΔ (1/2) Το αντικείμενο DatabaseMetaData παρέχει πληροφορίες για το σύστημα ΒΔ και για τον κατάλογο (catalog) DatabaseMetaData md = co.getmetadata(); // Prit iformatio about the driver System.out.pritl( Name: +md.getdrivername()+ versio: +md.getdriverversio()); Δουλκερίδης 25
Πρόσβαση στα Μεταδεδοµένα της ΒΔ (2/2) DatabaseMetaData md = co.getmetadata(); ResultSet trs = md.gettables(ull,ull,ull,ull); Strig tablename; while (trs.ext()) { // prit table ame tablename = trs.getstrig( TABLE_NAME ); System.out.pritl( Table: +tablename); // prit colum ames ResultSet crs = md.getcolums(ull,ull,tablename,ull); while (crs.ext()) { System.out.pritl(crs.getStrig( COLUMN_NAME )+, ); } } public java.sl.resultset gettables(java.lag.strig catalog, java.lag.strig schema, java.lag.strig table, java.lag.strig[] types) Δουλκερίδης 26
Ένα Πλήρες Παράδειγµα Strig url = jdbc:microsoft:slserver://labsql;databasename=northwid; User=OMADA20;Password=PWD20; Coectio co = DriverMaager.getCoectio(url); Statemet stmt = co.createstatemet(); Strig uery = SELECT ame, ratig FROM Sailors ; ResultSet rs = stmt.executequery(uery); try { while (rs.ext()) { Strig s = rs.getstrig( ame ); it = rs.getit( ratig ); System.out.pritl(s+ +); } } catch (SQLExceptio ex) { System.out.pritl(ex.getMessage()+ +ex.getsqlstate()+ + ex.geterrorcode()); } Δουλκερίδης 27
Επιπλέον Θέµατα
Χρήσιμες έννοιες για την ανάπτυξη εφαρμογών ΒΔ Προστασία από SQL ijectio Αποθηκευμένες διαδικασίες (stored procedures) Εναύσματα (triggers) Δουλκερίδης 29
SQL Ijectio Εισάγετε το όνομά σας: Strig usrid ß παίρνει τιμή από μια φόρμα Strig uery = SELECT * FROM Users WHERE UsrName = + usrid + Σενάριο κανονικής λειτουργίας (χωρίς κακόβουλους χρήστες) usrid = cdoulk Σε περιβάλλον όπου υπάρχουν κακόβουλοι χρήστες usrid = cdoulk or 1 = 1 (πρόκειται απλά για ένα παράδειγμα SQL ijectio, υπάρχουν και άλλα) Τρόπος αντιμετώπισης Χρήση PreparedStatemet με παραμέτρους Δουλκερίδης 30
SQL Ijectio PreparedStatemet ΛΑΘΟΣ ΧΡΗΣΗ PreparedStatemet p = ull; p = co.createstatemet( SELECT * FROM Users WHERE UserId = + usrid); p.execute(); ΣΩΣΤΗ ΧΡΗΣΗ PreparedStatemet p = ull; p = co.createstatemet( SELECT * FROM Users WHERE UserId =? ); p.setstrig(1, usrid); p.execute(); Δουλκερίδης 31
Αποθηκευµένες Διαδικασίες (Stored Procedures) Μια αποθηκευμένη διαδικασία είναι ένα πρόγραμμα που εκτελείται μέσω μιας μόνο εντολής SQL, που μπορεί να εκτελεστεί τοπικά και να ολοκληρωθεί μέσα στο χώρο διεργασιών του διακομιστή της βάσης Πλεονεκτήματα Βελτιωμένη απόδοση (σε αντίθεση με την εναλλακτική του λογικού δρομέα που δεσμεύσει πόρους όπως κλειδαριές και μνήμη) Επαναχρησιμοποίηση από διαφορετικούς χρήστες/προγράμματα Διαχωρισμός κώδικα εφαρμογής από κώδικα SQL Ανεξαρτησία από γλώσσα προγραμματισμού Βελτιωμένη απόδοση (λόγω του precompilatio) Δουλκερίδης 32
Παράδειγµα Δημιουργία/Ορισμός αποθηκευμένης διαδικασίας στο ΣΔΒΔ CREATE PROCEDURE myfirstsp SELECT C.cid, C.came, COUNT(*) FROM Customers C, Orders O WHERE C.cid=O.cid GROUP BY C.Cid, C.came Κλήση από JDBC CallableStatemet c = co.preparecall( {call myfirstsp} ); ResultSet rs = c.executequery(); while (rs.ext()) Δουλκερίδης 33
Παράδειγµα µε Ορίσµατα Δημιουργία/Ορισμός αποθηκευμένης διαδικασίας στο ΣΔΒΔ CREATE PROCEDURE myfirstsp( IN age INTEGER) SELECT C.cid, C.came, COUNT(*) FROM Customers C, Orders O WHERE C.cid=O.cid AND C.age > age GROUP BY C.Cid, C.came Δουλκερίδης 34
Εναύσµατα (Triggers) Μια διαδικασία την οποία δηλώνει ο Διαχειριστής ΒΔ η οποία ενεργοποιείται αυτόματα από το ΣΔΒΔ κάθε φορά που συμβαίνουν μεταβολές ορισμένου τύπου στα δεδομένα Μια ΒΔ η δήλωση της οποίας συνδυάζεται με δηλώσεις εναυσμάτων ονομάζεται ενεργή ΒΔ Στην περιγραφή ενός εναύσματος υπάρχουν 3 διακριτά μέρη: Γεγονός: μια μεταβολή στη ΒΔ που ενεργοποιεί το έναυσμα Συνθήκη: ένα αίτημα ή ένας έλεγχος που εκτελείται με την ενεργοποίηση του εναύσματος Ενέργεια: μια διαδικασία η οποία εκτελείται όταν ισχύει η συνθήκη και ενεργοποιείται το έναυσμα Δουλκερίδης 35
Παράδειγµα CREATE TRIGGER iit_cout BEFORE INSERT ON Studets /* γεγονός */ DECLARE cout INTEGER; BEGIN /* ενέργεια */ cout := 0; END CREATE TRIGGER icr_cout AFTER INSERT ON Studets /* γεγονός */ WHEN (ew.age < 18) /* συνθήκη (ew είναι η νέα πλειάδα) */ FOR EACH ROW BEGIN /* ενέργεια */ cout := cout +1; END Δουλκερίδης 36