Databázy (2) Prednáška 02. Alexander Šimko

Veľkosť: px
Začať zobrazovať zo stránky:

Download "Databázy (2) Prednáška 02. Alexander Šimko"

Prepis

1 Databázy (2) Prednáška 02 Alexander Šimko

2 Contents I

3 Upozornenie Prezentovaný kód nie vždy správne ošetruje chyby a uzatvára objekty. Je to kompromis, aby sa dal dať na slajdy aspoň trochu čitateľne.

4 Section 1

5 pre rôzne typy aplikácii to môže byť rôzne

6 Podnikové (enterprise) aplikácie Typické operácie CRUD operácie nad entitami zobraz zoznam zákazníkov zobraz detail zákazníka aktualizuj, pridaj, odober zákazníka Zložitejšie doménové operácie prevod peňazí medzi účtami pripisovanie úrokov na účty účtovanie poplatkov za služby Reporty/Štatistiky počet nových zákazníkov sa jednotlivé kvartály zisky za jednotlivé služby po jednotlivých mesiacoch

7 Špagetový kód Určite nie ako špagetový kód kód, ktorý nie je dobre organizovaný do logických celkov je prepletený ako špagety nikto sa už v ňom nevyzná

8 Špagetový kód Špagetový kód Éra príkazu GOTO

9 Špagetový kód Špagetový kód Čo zle sa môže stať?

10 Špagetový kód Špagetový kód Éra bez GOTO Miešame dokopy spracovanie a kontrolu vstupu od používateľa generovanie výstupu (HTML, kreslenie čiar,...) vykonávanie doménovej logiky práca s databázou

11 Špagetový kód Špagetový kód CRUD customers id : integer first_name : varchar last_name : varchar address : varchar birth_number : varchar gender : char(1)

12 Špagetový kód Špagetový kód CRUD Zobrazenie zákazníka class GUI { TextField firstname; TextField lastname; TextField birthnumber; TextField address; ComboBox gender; Label message;..

13 Špagetový kód Špagetový kód CRUD Zobrazenie zákazníka void showuser(int id) { Connection c =... PreparedStatement s = c.preparestatement("select * FROM customers WHERE id =?"); s.setint(1, id); ResultSet r = s.executequery(); if (r.next() == true) { firstname.settext(r.getstring("first_name"); lastname.settext(r.getstring("last_name"); birthnumber.settext(r.getstring("birth_number"); address.settext(r.getstring("address"); gender.setvalue(r.getstring("gender"); else { message.settext("no such customer exists"); s.close(); c.close();

14 Špagetový kód Špagetový kód CRUD Pridávanie zákazníka void onaddclicked() { if (firstname.gettext().isempty()) { message.settext("first name cannot be empty"); return; if (lastname.gettext().isempty()) { message.settext("last name cannot be empty"); return;

15 Špagetový kód Špagetový kód CRUD Pridávanie zákazníka Connection c =... PreparedStatement s = c.preparestatement("select * FROM customers WHERE birthnumber =?"); s.setstring(1, birthnumber.gettext()); ResultSet r = s.executequery(); if (r.next() == true) { message.settext("customer with given birth number already exists"); r.close(); s.close(); c.close(); return; r.close(); s.close();

16 Špagetový kód Špagetový kód CRUD Pridávanie zákazníka s = c.preparestatement("insert INTO customers (first_name, last_name, address, gender) VALUES(?,?,?,?)"); s.setstring(1, firstname.gettext()); s.setstring(2, lastname.gettext()); s.setstring(3, address.gettext()); s.setstring(4, gender.getselectedvalue()); try { s.executeupdate(); message.settext("customer was successfully added."); catch (SQLException e) { message.settext("could not perform the operation. Please try again."); s.close(). c.close();

17 Špagetový kód Špagetový kód Prečo to takto nerobiť? veľmi zle sa udržuje vysoká šanca chýb, ktoré sa potom ťažko hľadajú a opravujú veľmi obtiažne sa dopĺňajú nové požiadavky nový človek nemá šancu kódu porozumieť predlžuje čas tvorby softvéru a jeho náklady

18 Špagetový kód Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live

19 Row Data Gateway Problém Prepletené GUI a prístup k databáze na rôznych miestach v GUI kóde spúšťame rovnaké/podobné SQL príkazy názvy stĺpcov máme na rôznych miestach v GUI ak zmeníme názov tabuľky/stĺpca a pod, musíme meniť kód na veľa miestach navyše sú názvy tabuliek/stĺpcov ako reťazcové literály, čiže strácame statickú kontrolu kompilátorom a možnosť refaktorovania pomocou IDE

20 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? customers id : integer first_name : varchar last_name : varchar address : varchar birth_number : varchar gender : char(1) class Customer { Integer id; String firstname; String lastname; String address; String birthnumber; String gender;

21 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? static Customer findbyid(int id) { Connection c =... PreparedStatement s = c.preparestatement("select * FROM customers WHERE id =?"); s.setint(1, id); return loadone(s); static Customer findbybirthnumber(string birthnumber) { Connection c =... PreparedStatement s = c.preparestatement("select * FROM customers WHERE birth_number =?"); s.setint(1, birthnumber); return loadone(s);

22 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? static Customer loadone(preparedstatement s) { ResultSet r = s.executequery(); if (r.next()) { return load(r); else { return null; static Customer load(resultset r) { Customer customer = new Customer(); customer.id = r.getint("id"); customer.firstname = r.getstring("first_name"); customer.lastname = r.getstring("last_name"); customer.address = r.getstring("address"); customer.birthnumber = r.getstring("birth_number"); customer.gender = r.getstring("gender"); return customer;

23 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? static List<Customer> findall() { Connection c =... PreparedStatement s = c.preparestatement("select * FROM customers"); return loadall(s); static List<Customer> loadall(preparedstatement s) { ResultSet r = s.executequery(); List<Customer> customers = new ArrayList<>(); while (r.next()) { customers.add(load(r)); return customers;

24 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? void insert() { Connection c =... PreparedStatement s = c.preparestatement("insert INTO customers (first_name, last_name, address, birth_number, gender) VALUES(?,?,?,?,?) RETURING id"); s.setstring(1, firstname); s.setstring(2, lastname); s.setstring(3, address); s.setstring(4, birth_number); s.setstring(5, gender); ResultSet r = s.executequery(); if (r.next()) { this.id = r.getint("id"); r.close(); s.close();

25 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? void update() { Connection c =... PreparedStatement s = c.preparestatement("update customers SET first_name =?, last_name =?, address =?, birth_number =?, gender =? WHERE id =?"); s.setstring(1, firstname); s.setstring(2, lastname); s.setstring(3, address); s.setstring(4, birth_number); s.setstring(5, gender); s.setint(6, id); s.executeupdate(); s.close();

26 Row Data Gateway Čo takto zaobaliť prístup k DB do samostatnej triedy? void delete() { PreparedStatement s = c.preparestatement("delete customers WHERE id =?"); s.setint(1, id); s.executeupdate(); s.close();

27 Row Data Gateway Vylepšený kód CRUD Zobrazenie zákazníka class GUI { TextField firstname; TextField lastname; TextField birthnumber; TextField address; ComboBox gender; Label message;..

28 Row Data Gateway Vylepšený kód CRUD Zobrazenie zákazníka void showuser(int id) { Customer c = Customer.findById(id); if (c!= null) { firstname.settext(c.firstname); lastname.settext(c.lastname); birthnumber.settext(c.birthnumber); address.settext(c.address); gender.setvalue(c.gender); else { message.settext("no such customer exists");

29 Row Data Gateway Vylepšený kód CRUD Pridávanie zákazníka void onaddclicked() { if (firstname.gettext().isempty()) { message.settext("first name cannot be empty"); return; if (lastname.gettext().isempty()) { message.settext("last name cannot be empty"); return;

30 Row Data Gateway Vylepšený kód CRUD Pridávanie zákazníka Customer existing = Customer.findByBirthNumber(birthNumber.getText()); if (existing!= null) { message.settext("customer with the given birth number already exists"); return; Customer customer = new Customer(); customer.firstname = firstname.gettext(); customer.lastname = lastname.gettext(); customer.address = address.gettext(); customer.gender = gender.getselectedvalue(); customer.insert();

31 Row Data Gateway Čo sme tým získali? kód je prehľadnejší všetok SQL kód týkajúci sa tabuľky customers je v triede Customer čisto databázové zmeny sa prejavia iba v zmene tejto triedy máme statickú kontrolu názvov stĺpcov, nakoľko sú to teraz členské premenné triedy trieda Customer slúži aj ako dokumentácia hovoríme ňou, že systém narába so zákazníkmi

32 Row Data Gateway To, čo sme si práve ukázali je vzor Row Data Gateway je to architektonický vzor na oddelenie kódu na prístup k databáze pre jednu tabuľku vytvoríme jednu Row Data Gateway triedu inštancia triedy reprezentuje jeden riadok tabuľky členské premenné mapujú stĺpce tabuľky 1:1 RowDataGateway column1... dolumnn insert() update() delete() uses Finder findbyid(id) findbycolumn1(value) metódy na nájdenie riadku/ov v špeciálnej triede Finder alebo ako statické metódy triedy RowDataGateway

33 Row Data Gateway Row Data Gateway súvisiace argumenty pokope na výpočet oslovenia osoby potrebujeme viac atribútov zákazníka namiesto viacerých argumentov použijeme triedu Customer class Service { String getaddressline(customer c, String language) { if("en".equals(language)) { if (/* is male */) { return "Mr " + c.firstname + " " + c.lastname; else if (/* is younger that 15 */) { return "Miss " + c.firstname + " " + c.lastname; else { return "Ms " + c.firstname + " " + c.lastname;...

34 Row Data Gateway Zapúzdrenie členských premenných zmena členských premenných na private zavedenie public metód getxy a setxy

35 Row Data Gateway Zapúzdrenie členských premenných Kontrola argumentov class Customer { private String birthnumber; public String getbirthnumber() { return birthnumber; public void setbirthnumber(string birthnumber) { if (birthnumber == null) { throw new IllegalArgumentException(); if (birthnumber.matches("[0-9]{6/[0-9]{3,4" == false) { throw new IllegalArgumentException(); this.birthnumber = birthnumber;...

36 Row Data Gateway public void setgender(string gender) { this.gender = gender.equals("m")? Gender.MALE : Gender.FEMALE;... Zapúzdrenie členských premenných Zmena reprezentácie môžeme zmeniť vnútornú reprezentáciu bez rozbitia zvyšku kódu enum Gender { MALE, FEMALE class Customer { private Gender gender; // povodne String, teraz Gender public String getgender() { return gender == Gender.MALE? "M" : "F";

37 Row Data Gateway Zapúzdrenie členských premenných Vypočítané stĺpce class Customer { private String firstname; private String lastname; public String getfirstname() { return firstname; public String getlastname() { return lastname; public String getfullname() { return String.format("%s %s", firstname, lastname);... k pôvodným a vypočítaným stĺpcom pristupujeme rovnako

38 Row Data Gateway Cudzie kľúče ostávajú tak ako sú customers id : integer... transfer_limit : numeric accounts number : varchar balance : numberic overdraft : boolen owner_id : integer class Account { String number; BigDecimal balance; boolean overdraft; int ownerid; // toto je primarny kluc // toto je cudzi kluc...

39 Row Data Gateway Väzobné tabuľky sú tiež iba tabuľky customers id : integer first_name : varchar last_name : varchar... customer-badges id : integer customer_id : integer badge_id : integer badges id : integer name : varchar class CustomerBadge { Integer id; int customerid; int badgeid;...

40 Row Data Gateway Hodí sa zaobaliť aj vypočítané tabuľky (napr. štatistiky) SELECT ceil(balance / 100.0)*100.0 AS upper_bound, count(*) FROM accounts GROUP BY upper_bound ORDER BY upper_bound class RangeStatistic { BigDecimal upperbound; int count; static List<RangeStatistic> findall() { Connection c =... PreparedStatement s = c.preparestatement("select ceil...");...

41 Row Data Gateway Potom treba prehodnotiť či majú pre nás zmysel operácie insert update delete to závisí od kontrétneho prípadu, čo naozaj potrebujeme

42 Row Data Gateway Čo s nejakým špecifickým SQL príkazom? Napr. UPDATE, čo modifikuje tisíce riadkov Vyriešiť obdobne ako metódu find zaobaliť do metódy dať ju ako statickú do Row Data Gateway alebo do špeciálnej na to určenej triedy

43 Špagetový kód pokračuje Špagetový kód Prevod peňazí customers id : integer... transfer_limit : numeric accounts number : varchar balance : numberic overdraft : boolen owner_id : integer prevod peňazí medzi zdrojovým a cieľovým účtom kontrola, či je na zdrojovom účte dosť peňazí kontrola, či neprekračujeme limit nastavený majiteľom účtu

44 Špagetový kód pokračuje Špagetový kód Prevod peňazí class GUI { TextField sourcefield; TextField destinationfield; TextField amountfield; Label message; void ontransferclicked() { String source = sourcefield.gettext(); String destination = destinationfield.gettext(); BigDecimal amount = new BigDecimal(amountField.getText());

45 Špagetový kód pokračuje Špagetový kód Prevod peňazí Connection c =... PreparedStatement s = c.preparestatement("select * FROM accounts WHERE number =?"); s.setstring(1, source); ResultSet r = s.executequery(); if (r.next() == false) { message.settext("source account does not exist"); r.close(); s.close(); c.close(); return;

46 Špagetový kód pokračuje Špagetový kód Prevod peňazí BigDecimal sourcebalance = r.getbigdecimal("balance"); boolean overdraft = r.getboolean("overdraft"); integer ownerid = r.getint("owner_id"); r.close(); PreparedStatement cs = c.preparestatement("select * FROM customers WHERE id =?"); cs.setinteger(1, ownerid); r = cs.executequery(); r.next(); BigDecimal transferlimit = r.getbigdecimal("transfer_limit"); r.close(); cs.close();

47 Špagetový kód pokračuje Špagetový kód Prevod peňazí if (amount.compareto(transferlimit) > 0) { message.settext("transfer limit exceeded"); s.close(); c.close(); return; if (overdraft == false sourcebalance.compareto(amount) < 0) { message.settext("insufficient banance on the source account"); s.close(); c.close(); return;...

48 Špagetový kód pokračuje Prvé zlepšenie Row Data Gateway class GUI { TextField sourcefield; TextField destinationfield; TextField amountfield; Label message; void ontransferclicked() { String source = sourcefield.gettext(); String destination = destinationfield.gettext(); BigDecimal amount = new BigDecimal(amountField.getText());

49 Špagetový kód pokračuje Prvé zlepšenie Row Data Gateway Account sourceaccount = Account.find(source); if (sourceaccount == null) { message.settext("source account does not exist"); return; Account destinationaccount = Account.find(destination); if (destinationaccount == null) { message.settext("destination account does not exist"); return; Customer customer = Customer.find(sourseAccount.ownerId); if (amount.compareto(customer.transferlimit) > 0) { message.settext("transfer limit exceeded"); return;

50 Špagetový kód pokračuje Prvé zlepšenie Row Data Gateway if (sourceaccount.overdraft == false sourceaccount.balance.compareto(amount) < 0) { message.settext("insufficient balance on the source account"); return; sourceaccount.balance = sourceaccount.balance.substract(amount); destinationaccount.balance = destinationaccount.balance.add(amount); sourceaccount.update(); destinationaccount.update(); message.settext("transfer succeeded");

51 Transaction Script Problém Prepletené GUI a doménová logika Tá istá metóda rieši: ako získať vstup od používateľa ako vykonať doménovú logiku ako zobraziť doménové chyby používateľovi Ak budeme chcieť vykonať tú istú doménovú operáciu z iného používateľského rozhrania, musíme duplikovať kód.

52 Transaction Script Čo takto osamostatniť doménovú operáciu? class Banking { static void transfer(string source, String destination, BigDecimal amount) { Account sourceaccount = Account.find(source); if (sourceaccount == null) throw new Exception("Source account does not exist"); Account destinationaccount = Account.find(destination); if (destinationaccount == null) throw new Exception("Destination account does not exist"); Customer customer = Customer.find(sourseAccount.ownerId); if (amount.compareto(customer.transferlimit) > 0) throw new Exception("Transfer limit exceeded");

53 Transaction Script Čo takto osamostatniť doménovú operáciu? if (sourceaccount.overdraft == false sourceaccount.balance.compareto(amount) < 0) throw new Exception("Insufficient balance on the source account"); sourceaccount.balance = sourceaccount.balance.substract(amount); destinationaccount.balance = destinationaccount.balance.add(amount); sourceaccount.update(); destinationaccount.update();

54 Transaction Script Čo takto osamostatniť doménovú operáciu? class GUI { TextField sourcefield; TextField destinationfield; TextField amountfield; Label message; void ontransferclicked() { String source = sourcefield.gettext(); String destination = destinationfield.gettext(); BigDecimal amount = new BigDecimal(amountField.getText()); try { Banking.transfer(source, destination, amount); message.settext("transfer succeeded"); catch(exception e) { message.settext(e.getmessage());

55 Transaction Script To, čo sme si práve ukázali je vzor Transaction Script je to architektonický vzor na oddelenie kódu doménových operácii jedna operácia/požiadavka jedna procedúra je to také procedurálne programovanie v OOP jazykoch to skončí na tiedach, ktoré majú iba (statické) metódy a žiadne členské premenné/atribúty TransactionScript operation(argument1,..., argumentn) Alterantívy buď každý skript ako jediná metóda v samostatnej triede alebo nejako rozumne zoskupiť viac metód do jednej triedy

56 Transaction Script Čo sme tým získali? kód je prehľadnejší doménová operácia je oddelená môžeme ju volať z viacerých miest bez duplicity kódu zmeny v tejto operácii sa prejavia v kóde iba na jednom mieste trieda Banking a metóda transfer slúžia aj ako dokumentácia hovoríme nimi, že systém vykonáva takú operáciu

57 Rekapitulácia Je vhodné oddeliť kód realizujúci prístup k DB vzor Row Data Gateway doménové operácie vzor Transaction Script využíva Row Data Gateway používateľské rozhranie využíva Transaction Script využíva Row Data Gateway

58 Ešte jeden problém Aktuálny stav kód je síce rozdelený prístup k DB a doménové operácie bežia na tom istom počítači ako GUI

59 Ešte jeden problém Aktuálny stav jeden PostgreSQL server s jednou databázou viac klientských počítačov, na ktorých beží doménový kód server PostgreSQL Banková aplikácia Banková aplikácia Banková aplikácia klientské PC klientské PC klientské PC

60 Ešte jeden problém Aké to má nevýhody? citlivý kód ako prevod peňazí beží u klienta program sa dá dekompilovať a upraviť, aby robil niečo iné program sa dá dekompilovať, a tak zdrojové kódy nie sú chránené pred ukradnutím ak chceme mať dva internet bankingy, desktopový a mobilný, v oboch klientoch máme kód na prevod peňazí

61 Back end a front end Čo takto rozdeliť program na dva? back end serverová aplikácia implementuje doménovú logiku cez HTTP príjima volania metód a posiela odpovede front end desktopová/mobilná aplikácia implementuje používateľské rozhranie cez HTTP volá metódy backendu

62 Back end a front end class Backend { static void main() { // bezi na localhost:8000 HttpServer server = HttpServer.create(new InetSocketAddress(8000),0); server.createcontext("/transfer", new TransferHandler()); server.setexecutor(executors.newcachedthreadpool()); server.start(); class TransferHandler implements HttpHandler { void handle(httpexchange exchange) { String query = exchange.getrequesturi().getquery(); String source = // vytiahni argument z query String destination // vytiahni argument z query BigDecimal amount = // vytiahni argument z query Banking.transfer(source, destination, amount); // do exchange vypis ze sa prevod podaril Ukážka jednoduchého backendu

63 Back end a front end Ukážka jednoduchého frontendu class GUI { TextField sourcefield; TextField destinationfield; TextField amountfield; Label message; void ontransferclicked() { String message = Unirest.get(" sourcefield.gettext()).querystring("destination", destinationfield.gettext()).querystring("amount", amountfield.gettext()).asstring().getbody(); message.settext(message);

64 Back end a front end Nový stav server s backendom pripájajúcim sa na PostgreSQL veľa inštancií frontendu server PostgreSQL Back end Front end Front end Front end klientské PC klientské PC klientské PC

65 Back end a front end Backend musí spúštať príkazy paralelne každá požiadavka je v backende obslúžená v samostatnom vlákne JDBC spojenia sú síce threadsafe, ale blokujúce (prístup z viacerých vlákien) každé vlánko potrebuje vlastné spojenie

66 Back end a front end Zakaždým vytvoríme nové spojenie class TransferHandler implements HttpHandler { void handle(httpexchange exchange) { Connection c = datasource.getconnection();... c.close();

67 Back end a front end Predávanie si spojenia pripomenutie z minula class DbContext { private static Connection connection; private DbContext() {; // tymto zakazeme robit instancie static void setconnection(connection c) { connection = c; static Connection getconnection() { return connection; static void closeconnection() { if (connection!= null) { connection.close(); connection = null;

68 Back end a front end Predávanie si spojenia pripomenutie z minula class DbContext { private static Connection connection; // do statickej premennej connection vieme ulozit iba jednu hodnotu

69 Uchovávanie si spojení vo viacvláknovom prostredí java.lang.threadlocal je to úložisko jednej hodnoty T get() void set(t value) každé vlákno ale vidí iba hodnotu, ktorú zapísalo ono

70 Uchovávanie si spojení vo viacvláknovom prostredí java.lang.threadlocal ThreadLocal<Integer> value = new ThreadLocal<>(); Thread 1 Thread value.set(10) value.set(12) value.get() == 10 value.get() == 12

71 Uchovávanie si spojení vo viacvláknovom prostredí java.lang.threadlocal // toto je iba ilustracna zjednodusena implementacia class ThreadLocal<T> { void set(t value) { // kazde vlakno ma priradenu svoju mapu Map values = Thread.getCurrent().getThreadLocalMap(); values.put(this, value); void T get() { Map values = Thread.getCurrent().getThreadLocalMap(); return values.get(this);

72 Uchovávanie si spojení vo viacvláknovom prostredí DbContext s ThreadLocal class DbContext { static ThreadLocal<Connection> connection = new ThreadLocal<>(); private DbContext() {; // tymto zakazeme robit instancie static void setconnection(connection c) { connection.set(c); static Connection getconnection() { return connection.get(); static void closeconnection() { if (connection.get()!= null) { connection.get().close(); connection.set(null);

73 Uchovávanie si spojení vo viacvláknovom prostredí DbContext s ThreadLocal class TransferHandler implements HttpHandler { void handle(httpexchange exchange) { String query = exchange.getrequesturi().getquery(); String source = // vytiahni argument z query String destination // vytiahni argument z query BigDecimal amount = // vytiahni argument z query // ziskame nove spojenie a nastavime ho pre vlakno DbContext.setConnection(dataSource.getConnection()); Banking.transfer(source, destination, amount); // uzavrie spojenie pre svoje vlakno DbContext.closeConnection();

74 Uchovávanie si spojení vo viacvláknovom prostredí DbContext s ThreadLocal nemusíme stále opakovať argument Connection každé vlákno si uchováva svoje vlastné spojenie vhodné aj pre viacvláknovú aplikáciu

75 Uchovávanie si spojení vo viacvláknovom prostredí Odkiaľ sa vezme DataSource? class TransferHandler implements HttpHandler { void handle(httpexchange exchange) { String query = exchange.getrequesturi().getquery(); String source = // vytiahni argument z query String destination // vytiahni argument z query BigDecimal amount = // vytiahni argument z query // odkial sa vezme datasource? DbContext.setConnection(dataSource.getConnection()); Banking.transfer(source, destination, amount); DbContext.closeConnection();

76 Uchovávanie si spojení vo viacvláknovom prostredí static void closeconnection() { if (connection.get()!= null) { connection.get().close(); connection.set(null); DataSource v DbContext class DbContext { static DataSource ds; static ThreadLocal<Connection> connection = new ThreadLocal<>(); static void setdatasource(datasource d) { ds = d; static Connection getconnection() { if (connection.get() == null) { connection.set(ds.getconnection()); return connection.get();

77 Uchovávanie si spojení vo viacvláknovom prostredí Backend Vytvorenie DataSource class Backend { static void main() { // bezi na localhost:8000 PgSimpleDataSource ds = new PgSimpleDataSource(); ds.setservername("server name"); ds.setservername("database name"); ds.setportnumber(5432); ds.setuser("user"); ds.setpassword("password"); DbContext.setDataSource(ds); HttpServer server = HttpServer.create(new InetSocketAddress(8000),0); server.createcontext("/transfer", new TransferHandler()); server.setexecutor(executors.newcachedthreadpool()); server.start();

78 Uchovávanie si spojení vo viacvláknovom prostredí Backend Vytváranie a zatváranie spojenia class TransferHandler implements HttpHandler { void handle(httpexchange exchange) { String query = exchange.getrequesturi().getquery(); String source = // vytiahni argument z query String destination // vytiahni argument z query BigDecimal amount = // vytiahni argument z query // cez DbContext.getConnection() // ziska nove spojenie Banking.transfer(source, destination, amount); // zatvori spojenie DbContext.closeConnection();

79 Uchovávanie si spojení vo viacvláknovom prostredí Koniec Koniec