Java Programmering - 13. del

Tags:    java programmering database mysql
Skrevet af Martin Rohwedder @ 17.02.2012

Indledning


I denne artikel skal vi lære om hvordan man får java til at arbejde sammen med en database. Hvis du ikke allerede er skiftet over til at bruge en IDE (Integrated Development Environment), så kan jeg klart anbefale dig det nu, da det gør skrivning af din kode meget lettere, og jeg vil våge at påstå at du nu har nok basal Java viden til at kunne benytte dig af intellisence, som disse IDE's ofte tilbyder. Jeg bruger Netbeans, men du kan selvfølgelig også benytte andre IDE's det er lidt op til din egen smag. Den database jeg vil bruge her, er databasen MySQL. Jeg har valgt netop denne, fordi den er let tilgængelig for alle, og samtidig en af de mest populære open source databaser på markedet. For at kunne få mest ud af læringen i denne artikel, vil det være en god ide at kigge lidt på et andet (meget simpelt) sprog, kaldet SQL. Sproget SQL står for Structured Query Language, og er det sprog man bruger for at sende forespørgsler til ens database (En forespørgsel er når man beder databasen om at hente, indsætte, opdatere eller måske slette data). Da jeg ikke vil gennemgå særlig meget af teorien i SQL, vil det være en god ide at læse lidt om dette først, men frygt dog ej, hvis du kun har brugt SQL meget få gange, da jeg vil prøve at forklarer min SQL kode så godt som muligt. Jeg har til formålet fundet nogle referencer omkring SQL, som du evt. kan kigge på, før du går videre med denne artikel.Jeg vil i denne artikel lave et simpelt java program, hvor vi får lavet en forbindelse til databasen, og hentet noget data ind i vores program. Dette er bare for at få os i gang, og nemt kan se hvordan det hele virker. Herefter vil jeg lave et eksempel på hvordan du med java også kan indsætte til databasen, og til dette eksempel vil jeg også kort komme ind på database transaktioner og hvordan disse benyttes i java. Til allersidst i denne artikel, vil jeg prøve at kombinerer noget af den viden vi allerede har tilegnet os omkring GUI programmering, og bruge den med den viden vi lærer her om databaser, så du kan se hvordan du evt. kan benytte den sammen med en grafisk brugergrænseflade.

Klargør en MySQL database


Trin 1: Hvis du ikke allerede har en mysql database installeret på din pc, skal du gøre dette. Du kan hente den nyeste version af MySQL databasen på - http://www.mysql.com/downloads/mysql/ -Hvis du bruger windows styresystem, skal du vælge den download der hedder MSI installer, da dette er den nemmeste måde at installerer MySQL databasen på. Hvis du benytter en Machintos, kan du vælge dmg filen, som fungerer lidt ligesom MSI installeren. Bemærk at du både skal installerer selve mysql samt startupitems, hvis du benytter en mac, da du så nemt kan starte din database via systemindstillingerne på din mac.

Trin 2: Hent nu MySQL workbench, som er et GUI værktøj, så du nemt kan administrerer dine mySQL databaser. Du kan hente dette på siden - http://www.mysql.com/downloads/workbench/ - og vælg også her MSI installeren, eller dmg filen hvis du benytter en mac.

Trin 3: Installer nu først MySQL databasen og herefter workbenchen. Bemærk at på windows vil den formentlig spørge dig om du vil give et password til root brugeren. Dette er valgfrit, og brugeren har som default intet password, men det er altid en god ide at sætte et password til denne bruger. Du kan vælge hvad du har lyst til, men i min eksempler vil jeg sætte min root password til at være test. Benytter du en mac for du formentlig ikke denne valgmulighed under installationen af MySQL, og du er derfor nødt til at sætte root brugerens password manuelt. Måden du gør dette på er:
  1. Start teminalen ved at vælge den i programmer -> hjælpeværktøjer -> terminal
  2. Ændrer passwordet ved at skrive følgende i terminalen - /usr/local/mysql/bin/mysqladmin -u root password DitKodeordHer - Du skal selvfølgelig udskifte DitKodeordHer, med det du ønsker kodeordet skal være.
  3. Du bliver nok spurgt om et password nu, og her skal du bare skrive dit mac password, som du oprettede, da du installerede Mac Os X på din mac.
Nu skulle du gerne have ændret root brugerens kodeord. samt installeret en MySQL database, med tilhørende workbench (GUI værktøj), som du kan bruge til at 'administrerer' dine MySQL databaser i.

Opret en MySQL database


Nu da vi har anskaffet os MySQL, kan vi begynde at oprette vores allerførste MySQL database. Åben din workbench og vælg nu 'database -> query database' fra menuen i toppen. Herefter åbnes følgende vindue



Dette vindue er den der åbner en forbindelse til vores MySQL. Brugeren root, har et hostname der hedder localhost eller 127.0.0.1 (Begge er localhosts). Du behøver som regel ikke at ændrer noget i dette vindue, men skal bare klikke på ok. Hvis du nu får en 'fejl besked', så er det ofte fordi du ikke har husket at starte MySQL serveren. På din mac kan du starte denne ved at åbne system indstillinger, og klikke på MySQL, hvis du vel og mærke har husket at installerer startupitems. På windows vil MySQL ofte starte automatisk, når du bruger workbench, men gør den ikke dette, kan du finde hjælp til at starte MySQL fra terminalen her. Hvis ikke der kommer en fejl meddelelse vil du formentlig blive bedt om at skrive kodeordet til root brugeren, som jo var det du lavede da du installerede MySQL. Nu skulle din workbench gerne åbne et vindue lidt som dette.



I dette vindue kan du skrive SQL kode, for at oprette nye databaser, og tilføje tabeller til disse, samt indsætte data i tabellerne, hvis dette er nødvendigt. Jeg finder det nemmest at benytte SQL når jeg skal oprette databaser, men du kan vist også oprette databaser mere 'grafisk' ved brug af workbench. Dette må du dog selv ligge lidt og lege med.

Opret vores database


Lad os nu sige at vi gerne vil have en database hvor vi kan gemme informationer omkring hunde og deres ejere. Lad os sige at vi vil have en database med to tabeller, en for hunde og en for dens ejere. Lad os så sige at en hund altid kun har en ejer, mens en ejer kan 'eje'/have flere hunde. Vi har altså det man kalder for èn en-til-mange relation. Hvis du ikke er helt sikker på hvad en relation er i database, så betyder det ikke så meget for eksemplet, men hvis du gerne vil forstå hvordan koden om lidt kommer til at virke, så vil jeg anbefale dig at læse lidt om dette emne her.

For at gøre dette lidt hurtigt, vil jeg kort forklare hvad hele min SQL kode gør, og du kan så herefter bare kopiere denne ind i din workbench, for at oprette både databasen og tabellerne. Men kort fortalt vil følgende SQL kode altså oprette en database med navnet 'dogbase', samt to tabeller med navnene 'db_dogs' og 'db_owners'. Tabellen 'db_owners' indeholder her 4 kolonner, eller som man siger i fagsprog, attributter. Tabellen 'db_dogs' indeholder 3 attributter, hvoraf den ene af dem er en fremmednøgle der skal holde en reference/relation til en række i vores 'db_owner' tabel. Husk på at en ejer, kunne eje mere end en hund, men en hund vil altid kun have en ejer. Til sidst indsætter vi lidt test data i vores to tabeller, så vi kan begynde at arbejde med dem i vores java applikation.
Fold kodeboks ind/udSQL kode 

Klik nu på ikonet der ligner et lyn, og den vil herefter execute/køre din SQL kode. Hvis du kører ovenstående kode, skulle den gerne kører koden uden nogle fejl, men hvis du f.eks. kører din egen kode, og der er opstået en fejl, kan du altid i loggen under editoren se hvilken fejl der opstod. Din dogbase skulle nu gerne være oprettet, og hvis du ikke kan se databasen ude til venstre i workbenchen, så prøv at højreklikke i området til venstre, og vælg refresh all, og den skulle nu gerne være synlig.

Brug MySQL sammen med Java


Nu kommer vi til det mest spændende i denne artikel. Det er nemlig her hvor vi lærer hvordan vi kan oprette en forbindelse imellem Java og vores MySQL database, og på denne måde benytte vores data i databasen sammen med Java. Det første vi skal er at oprette en forbindelse til vores database, og dette kan vi opnå ved at lave en klasse i java med navnet DBConnectionBuilder. Denne klasse skal stå for at bygge en forbindelse til vores database. Den skal derfor bare returnerer et objekt af denne forbindelse. Da vi ikke behøver at oprette et objekt af denne klasse hver gang, kan vores getConnection() metode bare deklareres som en statisk metode. Metoden skal altså returnerer et objekt der indeholder vores forbindelse, og dette er forholdsvist simpelt i java, da java API'et har en pakke der hedder java.sql. I denne pakke kan vi finde alle mulige klasser, som vi kan benytte når vi skal arbejde med SQL og databaser. En af klasserne hedder f.eks. Connection og det er denne klasse som vores metode getConnection(), skal returnerer. Derfor skal datatypen selvfølgelig være denne. Indtil nu ser vores DBConnectionBuilder klasse således ud
Fold kodeboks ind/udJava kode 

For at lave en connection, har vi brug for nogle specielle indstillinger. Da denne klasse er en builder, har jeg faktisk allerede specificeret at det skal være en klasse som bygger connections, ikke kun til MySQL, men til alle slags databaser. Den implementerer nemlig det som man kalder for et Builder Design Pattern, eller rettere sagt, det er skal være dens formål. Nå men det var lidt et sidespring, men pointen var sådan set at vi kan lægge nogle data ind i en .properties fil (almindelig tekst fil, men med endelsen .properties). På den måde er det eneste du skal gøre at ændre i denne .properties fil, næste gang du ønsker at benytte DBConnectionBuilder klassen, måske endda med en helt ny database. De indstillinger vi har brug for at vide, når vi laver en connection, er en host, et username til vores database, et password til denne, samt databasens navn. Dette er dog ikke det eneste, da vi også har brug for at vide hvilken Java Database Connectivity vi har (JDBC), samt hvilken driver den skal benytte. Frygt ej, hvis du syntes dette lyder som en hel del, da du bare kan kopiere nedenstående fil, ind i din egen .properties fil.

DBConnection.properties
Fold kodeboks ind/udKode 

Nu kan vi gøre det at vi kan læse filens indhold ved hjælp af klassen FileInputStream, som vi finder i pakken java.io. Vi skal også bruge en klasse der hedder Properties, som vi finder i pakken java.util. Måden vi læser filen på er at læse filen ind i vores FileInputStream objekt, og herefter give dette objekt som parameter til Vores Properties load metode. Husk at det kan være nødvendigt at kaste exceptions videre, eller fange dem i metoden direkte, med en try catch blok. Det er op til dig, hvordan du vil gøre. For en god ordens skyld vil jeg lave læsningen af properties filen i en ny 'privat' metode.
Fold kodeboks ind/udJava kode 

Som du kan se har vi her indlæst vores DBConnection.properties i vores FileInputStream objekt. Dette objekt indlæser vi så i vores Properties objekt, så vi kan læse de enkelte properties nemt og elegant. Når vi læser dem, så lad os gemme værdierne i nogle klassevariabler, så vi kan benytte dem når vi skal oprette forbindelsen. Måden du læser filen på, er ved at benytter Properties objektets metode getProperty(), som kræver en parameter, nemlig navnet på vores property. Hvis vi f.eks. gerne vil hente vores brugernavn i DBConnection.properties filen, skal vi skrive metoden getProperty("username"), og den vil så returnere root, som er brugernavnet. Lad os se på hvordan vores kode ser ud nu.
Fold kodeboks ind/udJava kode 

Lige nu har vi en metode der læser vores properties fil, og vi skal nu bare kalde denne metode i vores getConnection() metode. Bemærk at du skal huske at rethrowe eller catche de to exception der kan komme fra vores setConnectionProperties() metode, det er op til dig hvordan du vil gøre dette. Lad mig lige opsummerer hvad det er der faktisk sker i metoden setConnectionProperties(). Metoden læser vores .properties fil, som indeholder nogle properties. Disse properties værdier, som den får ud, lægger den ind i nogle klassevariabler, som vi kan benytte i vores metode getConnection().

For at lave en connection til en database, skal vi først skrive linjen Class.forName( driver ); - Denne linje vil simpelthen gå ind og finde vores driver klasse, og gøre den en del af vores klasse. Denne metode vil dog smide en ClassNotFoundException, da det er muligt den ikke kan finde driver klassen. Herefter skal vi lave en forbindelse ved hjælp af klassen DriverManager, som findes i java.sql pakken. DriverManager klassen har en metode der hedder getConnection(), som kræver jdbc, host og db værdierne, samt brugernavn og kodeord. JDBC, Host og db skal sættes sammen til en streng, som ser således ud - jdbc + host + "/" + db, og dette gives som den første parameter, mens username og password gives som de to næste parametre. Bemærk at denne metode formentlig giver en SQLException, og denne skal du også håndtere ligesom de resterende exceptions. Koden ser nu således ud
Fold kodeboks ind/udJava kode 

Nu har vi lavet en klasse der bygger en database forbindelse. Dette kunne gøres langt mere enkelt, f.eks. ved at droppe vores properties fil, og skrive alle værdierne til vores properties direkte i vores DriverManager, men fordelen ved at benytte en properties, er at klassen nu kan bygge en forbindelse til en helt anden database en f.eks. MySQL. Det eneste man skal gøre er at ændre i properties filen, med navnet DBConnection.properties. På den måde bliver klassen mere transportabel, samt genbrugelig i fremtiden.

Vi kan nu oprette en ny klasse, som skal indeholde vores main metode. Vi kunne gå endnu længere og opbygge nogle andre klasser, som skulle være vores domæne klasser (hund og ejer), men da dette allerede er stort nok et projekt i forvejen, samt at det har taget tid, så vil jeg ikke begynde på dette. Main metoden skal kalde getConnection() metoden fra DBConnectionBuilder klassen, og herefter lave nogle ting med vores database, som f.eks. at hente data fra databasen. Lad os se på et eksempel, hvor vi henter alle ejere og deres hunde fra vores database.
Fold kodeboks ind/udJava kode 

Følgende kode kan ved første øjekast virke meget ny og uoverskuelig, men lad os dele den lidt op og kigge på den. Den første linje i main metoden benytter metoden getConnection() fra klassen DBConnectionBuilder, som jo giver os en forbindelse til vores database. Denne forbindelse gemmer vi i en lokal variabel (datatype Connection), med navnet conn. Næste linje er en lokal variabel af datatypen String, som indeholder vores SQL kode, der skal udføres. Lige netop denne kode henter (select) nogle rækker fra (from) vores to tabeller db_owners og (join) db_dogs, hvorpå (on) db_owners.owner_id = db_dogs.dog_owner. Orden dem efter (order by) owner_name fra lavest til størst (ascending). Vi kan nu indsætte denne sql streng i et statement. Dette gør vi ved at kalde en metode der hedder createStatement() fra vores connection. herefter kan vi kalde en metode fra det statement objekt vi får ud, som hedder executeQuery(). Denne kræver en parameter, der er vores sql streng. Bemærk at hvis du skal køre SQL kode der indsætter, opdaterer, eller sletter, så skal du benytte metoden executeUpdate() i stedet for executeQuery(). Denne udførelse af SQL koden resulterer i et ResultSet. Denne klasse indeholder en slags Set kollektion, med alle vores resultater. Først henter vi metadata'ene fra vores resultater, og nogle af de metadata man får er bl.a. kolonnernes navne i vores tabeller. Disse udskrives med et System.out.printf statement. Herefter kører vi en while løkke, der henter hver række i vores ResultSet, så vi kan benytte de data vi fik fra databasen. Metoden next() returnerer sand så længe at der er resultater tilbage i vores ResultSet. Vi henter altså inde i løkken hver enkelt række der blev fundet og udskriver denne. Til sidst skal vi huske at lukke vores database forbindelse, ved at kalde metoden close(). Alt dette er sat ind i en try/catch blok, for at kunne fange evt. exceptions der måtte ske under kørslen.

For at alt denne kode kan køre, skal vi huske at vedlægge en driver til MySQL databasen. En sådan driver kaldes for en J Connector, og det er en simpel jar fil, som du skal vedlægge i dit programs CLASSPATH. Denne J Connector kan du downloade på MySQL hjemmeside. Hvis du benytter netbeans, kan du nemt sætte J Connectoren til din CLASSPATH, ved at højreklikke på dit java projekts navn og vælge properties. Den åbner nu et nyt dialog vindue, og til højre i vinduet skal du vælge libraries. Dette vil vise dine 'compile-time libraries'. Der er formentlig ikke nogen tilknyttet lige nu, men du kan nem tilknytte din MySQL J Connector jar fil, ved at trykke på knappen 'Add Jar/folder', og herefter browse til jar filen i den nye mappe du lige har downloadet. Dette vil sikre at hver gang dit program går igennem compile og run, så følger dette jar bibliotek med, som er det der indeholder MySQL driveren. Klik nu ok til det hele og du skulle nu gerne have tilføjet MySQL J Connector jar filen til fit projekt. For at være helt sikker, kan du åbne folderen i dit projekt kaldet libraries og der vil formentlig være et billede af din nyligt tilføjede jar fil her. Nu er du klar til at køre vores lille test.

Resultatet af DBTest klassen vil se således ud i netbeans


Således kun man få java til at samarbejde med en database. I vores tilfælde en MySQL database. Dette var et simpelt eksempel, og er viden nok til at du kan arbejde med en database i dit eget program, men for at bygge lidt mere viden på, vil jeg også lige lave et eksempel hvor vi indsætter data til databasen med java. I mit eksempel vil jeg benytte en teknik som hedder transaktioner, og hvis du ikke ved hvad en transaktion er, så er det at man 'grupperer' nogle SQL statements, og først når alle sammen er udført korrekt sætter man det endelige stempel om at dataene i databasen skal gemmes. Det smarte ved dette, er at man har mulighed for i processen at afbryde det hele, og på den måde ikke risikere at nogle tabeller bliver opdateret med noget data, mens andre ikke gør. Hvis du gerne vil læse mere om hvordan dette virker, og hvorfor at det er så vigtigt at sikre sig imod (specielt i flerbruger databaser), så kan jeg anbefale denne artikel - DBMS Samtidigheds Protokoller - Locking & Timestamping - Men nu tilbage til vores java kode.
Fold kodeboks ind/udJava kode 

På linje 24 ser du denne metode setAutoCommit(false); - Denne metode er normalt sat til true. Det er den fordi vores dbms skal sørge for at comitte (udføre) hver sql statement hvor eneste gang denne udføres. Den vil altså automatisk kalde metoden commit(), hver gang vi kalder metoden execute() (eller måske executeUpdate()). Når denne sættes til false, skal vi altså selv sørge for at comitte når vi ønsker dette skal ske. Det smarte ved det er at vi kan lave en såkaldt transaktion, som er en gruppering af SQL statements, der alle hører sammen, og herved sikre princippet Atomicity (Jævnfør ACID modellen i artiklen jeg henviste til før), altså at alle sql statements skal udføres korrekt ellers skal ingen af vores sql statements udføres, de er altså en enkelt enhed (atom).

Det som vores main metode kort fortalt gør, er at bede brugeren om nogle data. Herefter laver den det første sql statement, hvor den indsætter de data brugeren kom med til databasen. Dette SQL statement udføres, og vi er nu klar til at lave den næste. Den næste er en SELECT SQL statement, hvor vi henter data fra databasen, og i vores tilfælde henter den owner_id'et på vores nyligt indsatte række, da vi skal bruge dette når vi skal indsætte data om hunden. Den sidste SQL statement indsætter så data om hunden, og når denne er udført kan vi kalde metoden commit(), som sikrer at alle de ændringer der lige er sket i databasen faktisk også er gyldige, så vores dbms (MySQL i dette tilfælde), skal altså nu gøre ændringerne permanente. Hvis du nu for sjov ændrede linje 86 i vores eksempel fra at være conn.commit();, til at være conn.rollback();, så ville vores database ikke udføre dine SQL statements, fordi du i sidste ende ikke lavede en commit(), men en rollback(), som betyder at den skal annulerer hele transaktionen, og sørge for at databasen er tilbage i dens sidst kendte tilstant, altså at den så ud som før du begyndte på transaktionen.

Dette var lidt om commit() og rollback(), men hvad er det egentlig der sker i vores kode. På linje 41 har vi vores SQL streng, som ser således ud - "INSERT INTO db_owners (owner_name, owner_age, owner_address) VALUES (?, ?, ?)" - Læg specielt mærke til VALUES delen, hvor vi har indsat tre spørgsmålstegn (?) i stedet for de værdier der skulle indsættes. Dette er fordi vi laver det der hedder et prepared statement, som er en måde at lave et statement, hvor værdierne ikke er kendte i forvejen. Alternativt kunne vi have lavet et almindeligt statement, hvor vi bare havde sat vores variabler ind direkte. Et eksempel på det vill have set således ud - "INSERT INTO db_owners (owner_name, owner_age, owner_address) VALUES ('" + ownerName + "', " + ownerAge + ", '" + ownerAddress + "')"[/ - Og hvorfor ikke bare gøre sådan, så kunne vi jo spare nogle linjers kode. Problemet er at sikkerheden bliver forringet ved at bruge det sådan, samt performancen bliver dårligere, end ved brug af prepared statements. Desuden skal man selv huske '' omkring strenge, samt andre konventioner, og dette slipper du også for at skulle huske da det sker automatisk med at prepared statement. På linje 45 laver vi så vores prepared statement, med den tilhørende SQL streng. Linjerne 48 - 50 sætter vi så spørgsmålstegnenes værdier ved at kalde den passende metode for den datatype der skal indsættes i vores database. Bemærk at vi først benytter metoden setString(1, ownerName), hvor den første parameter er nummeret på det første spørgsmåltegn (1, betyder at den skal tage det første spørgsmålstegn i vores SQL streng), og den anden parameter er vores data. Den næste hedder setInt(2, ownerAge), og det giver lidt sig selv, at her prøver vi ikke at indsætte en streng, men en integer. På linje 53 udfører vi så vores prepared statement, ved at bruge metoden executeUpdate(). Bemærk at når du skal indsætte, opdaterer, eller slette fra databasen, så skal du benytte metoden executeUpdate() og ikke executeQuery() eller execute(). Normalt ville java auto comitte vores data nu, og herved gemme dataene i databasen permanent, men fordi vi har sat denne til false, så gemmer den kun vores data i databasen midlertidigt, indtil vi manuelt kalder commit() metoden. Resten af koden er lidt ligesom denne jeg lige har gennemgået, men læg mærke til at jeg har tilføjet en finally del til min try catch blok. Jeg har gjort dette, fordi uanset om vores transaktion oplever commit() eller rollback, så skal jeg huske at lukke forbeindelsen. Det er god skik at huske dette, da en åben forbindelse kan ligge og opsluge memory. Faktisk er det også en god ide at lukke statements objekter, samt resultset objekter efter brugt, ved at kalde deres metode close(). Dette har jeg også gjort på linje 90 - 93.

Dog Base GUI Applikation med MySQL database


Som lovet ville jeg prøve at vise dig hvordan man evt. kunne lave en grafisk brugergrænseflade, og benytte denne sammen med ens database. Lad os forestille os at vi ønsker at lave et simpelt GUI, hvor vi skal have en slags browsing navigation øverst, hvor vi kan gå frem og tilbage i de rækker der findes i vores database. Herefter har vi en masse tekst felter, hvor der kan stå informationerne som vi har hentet ud af hver række, men disse skal samtidig fungere som tekst felter, man kan bruge for at indsætte en ny række til databasen. Knapperne der skal 'bestemme' om man skal hente alle rækker, eller indsætte en ny skal så være nedenunder dette. Vores Grafiske Brugergrænseflade skal atlså komme til at se således ud



Måden vi kan opbygge et sådan't GUI, er bl.a. ved at benytte os af nogle paneler/containere kaldet JPanels, samt nogle layout managere. Hvis du ikke helt kan huske hvad jeg mener, kan du tage et kig på - Java Programmering 8. Del - hvor jeg beskriver lidt om dette, samt hvordan du laver labels, tekst felter og knapper. Jeg skal selvfølgelig nok gennemgå min kode her, men jo mere du kan af de ting vi lærte i 8. del, jo nemmere kan du læse koden.

Det første vi skal er at lave en ny klasse, som skal indeholde vores GUI. Jeg har kaldt min for MainWindow, og indtil videre ser den således ud
Fold kodeboks ind/udJava kode 

Som du kan se, så har vi en main metode i denne klasse. Det der sker i main metoden er faktisk at vi opretter en ny tråd (mere om dette emne i en anden artikel), og i denne tråd prøver vi først at ændre Look & Feel til et design der hedder nimbus. Normalt er designet for java GUI appliaktioner et der hedder metal, men efter java 6 update 10, har man kunnet benytte dette L&F i stedet. Min personlige mening er at dette L&F er en hel del kønnere end det normale metal L&F. Vi opretter herefter et objekt af vores MainWindow klasse, som får vores GUI til at starte. I vores MainWindow klasse har vi så en kosntruktør, samt to private metoder. Den første hedder createMainWindow(), og i denne metode skal vi lave alt logik der opsætter, samt opretter vores GUI. Den næste metode setMainWindowSettings() skal sørge for at lave alle vores vindues settings, som f.eks. dens størrelse, dens placering på skærmen, samt hvad der skal ske når brugeren trykker på krydset i vinduets skærm. Denne metode kalder vi så i vores createMainWindow() metode, som igen bliver kaldt fra vores kosntruktør. Hvis du kører denne kode nu, vil den bare lave et tomt vindue, så vi skal have lavet noget GUI hertil. Hvis ikke du har lagt mærke til det, har jeg sat layout manageren til dette vindue til Flow Layout, med et alignment hvor komponenter alignes i center, samt et horisontalt og vertikalt gap/hul på 10 fra vores vindues sider. Så alle vores komponenter vil blive lagt i et flow, hvor første komponent starter i center af vores vindue.

Vi skal nu have lavet vores navigations panel,som skal indeholde en knap, et tekst felt, et label, endnu et tekst felt, samt endnu en knap. Vi kan nu lave en ny privat metode, som returnerer et JPanel, med alle disse ting, så vi nemt bare kan tilføje dette JPanel til vores frame. Husk dog på at vi skal kunne få fat i objekterne af henholdsvis knapperne og tekst felterne, så det vil være en god ide at lave disse som objekt variabler. Vores klasse ser nu således ud
Fold kodeboks ind/udJava kode 

Vi har her tilføjet to knapper og to tekst felter som objekt variabler. Dem kan vi så bruge og tilføje til vores navigations panel. Læg også mærke til at vi tilføjer vores navigations panel til selve vinduet på linje 40. Faktisk er der ikke så meget nyt i dette, andet end at vi her benytter en ny layout manager, som hedder box layout. Dette layout giver mulighed for at lægge vores komponenter som bokse, og i vores tilfælde lægge dem som bokse på x-aksen. Box.createHorizontalStrut( 10 ), laver en usynlig boks, med en bredde og højde på 10, som er med til at give en lille smule luft imellem vores enkelte komponenter.

Det næste vi skal lave er vores display panel, som er der hvor vi har vores mange tekst felter, så vi kan vise de enkelte rækker, samt indsætte en ny. Bemærk at dette JPanel benytter en anden layout manager, kaldet et grid layout, som sikrer at vi kan sætte vores komponenter op som i en tabel/grid. Vores kode ser altså nu således ud
Fold kodeboks ind/udJava kode 

Her har vi husket at importere den nye layout manager kaldet grid layout, samt lavet 5 nye tekst felter som objekt variabler. Vi kan så herefter lave metoden createDisplayPanel(), som laver vores display panel med et grid layout, af 2 kolonner og 5 rækker. Vi tilføjer så bare herefter komponenter (labels og tekst felter) til vores grid. Vi returnerer så display panelet og tilføjer det til vores vindue på linje 47.

Den næste del vi skal gøre er at lave vores panel, hvor vi kan finde en række ved hjælp af owner name. Der er heller ikke så meget i dette, foruden dog at vi tilføjer en kant rundt om vores panel. Dette gør vi ved hjælp af en klasse der hedder BorderFactory (som faktisk er en implementering af factory design pattern). Lad os lige se på koden nu
Fold kodeboks ind/udJava kode 

Nu har vi oprettet en ny knap, samt et nyt tekst felt, som objekt variabler, vi kan benytte i vores metode. Vi har herefter lavet en metode med navnet createQueryPanel(), som laver vores nye panel. Denne panel har en border/kant, som vi hørte før, var lavet ved hjælp af BorderFactory klassen. Det er en TitledBorder, som vi har valgt at lave, og dette er som navnet lidt siger en kant med en titel på. herefter er det bare at tilføje komponenterne efter hinanden, da vi også i dette panel benytter et box layout med placering af komponenterne på x-aksen.

Det eneste vi mangler nu, for at fulføre kreationen af vores GUI, er at tilføje to knapper (browse All Entries samt Insert New Entry) i bunden. Vi kan nemt bare tilføje disse direkte i metoden createMainWindow, da vi ikke behøver noget panel til disse. Vores GUI ser nu således ud
Fold kodeboks ind/udJava kode 

Hvis du kører koden nu, vil du se det GUI, som du så på billedet. Vi har dog den hage at vores applikation ikke rigtig kan noget endnu. Dette kan vi dog let rode bod på, ved at lave nogle button action listeners. Det er nogle action listeners der lytter på om der bliver klikket på vores knapper. Vi kan lave en ny privat metode der hedder addButtonClickListeners(), som tildeler hver kanp en action listener som har en anonym inner class. I denne anonyme inner class er der så en actionPerformed som kalder deres respektive event metode, ligesom vi har set det i bl.a. 8. Del. Vores kode ser altså således ud nu
Fold kodeboks ind/udJava kode 

Nu har vi gjort klar til at vores knapper kan udføre nogle actions/events, men før vi begynder at lave disse events, skal vi lige have lavet vores domæne klasser. Et domæne er ofte kendetegnet med det som systemet arbejder med. Hvis vi nu skulle udarbejde et lagersystem, kunne nogle af domæne klasserne være, produkt, produktgruppe, produktbeskrivelse, lager osv. Altså nogle 'fysiske' ting der modelerer vores system. Det kan dog også være knap så fysiske ting der udgør vores domæne, såsom ordre, og ordrelinjer (Hver linje på en ordre seddel). Domænet er altså kort fortalt en lille model af det system vi skal bygge. Ofte afspejler domænen databasen, og en sådan har vi jo allerede lavet. Vores database består af to tabeller, db_owners og db_dogs. Disse to tabeller kan vi lave om til domæne klasser, så vi bare skal hente data fra databasen ned til disse objekter, og vi kan nu nemmere arbejde med dataen i vores GUI. Da domæne klasserne i vores tilfælde kun skal 'afspejle' databasen, så vil jeg bare hurtigt lave domænet, men jeg vil kun lave en klasse, kaldet owner, da vores applikation faktisk fokuserer på denne og ikke på dogs. Vores domæne skal bare indeholde respektive objekt variabler, samt get og set metoder til disse variabler.

Owner.java
Fold kodeboks ind/udJava kode 

Vi kunne have lavet både en owner og en dog, man da vores applikation alligevel vil vise den samme 'owner', hvis denne har to tilhørende hunde, så vil dette ikke betyde helt så meget. Hvis du har lyst kan du dog prøve at lave to klasser i stedet for denne ene domæne klasse.

Det vi mangler nu er at genbruge vores DBConnectionBuilder klasse, samt lave en klasse der skal stå for at håndtere flowet imellem data hentet fra vores database, og ind i vores program. En sådan klasse kaldes ofte en Data Mapper klasse, efter at man faktisk benytter et design pattern der hedder Data Mapper Design Pattern. Kort fortalt fungerer det på den måde at vores event metoder i vores GUI klasse vil benytte domæne klasserne og sende disse videre til dens respektive Data mapper klasse. Data mapperen sørger så for at lave objektet om til relationel data, så vi kan gemme dette i databasen. Ligeledes når vi henter data fra databasen pakker vores data mapper de data den har fundet ind i et objekt fra vores domæne, og sender dette objekt videre til os. Vores Data mapper klasse har jeg valgt at kalde for DogBaseMapper, men hvis du har et større program end vores, så kan det være en god ide at splitte dine mappers op, f.eks. for hver database tabel du har osv.

Indtil videre ser vores data mapper således ud
Fold kodeboks ind/udJava kode 

Som du kan se har den tre 'tomme' metoder, men dette vil selvfølgelig ikke være tilfældet senere. Metoderne skal have hver deres formål/opgave, hvilket gerne skulle afspejles i deres metodenavne. Første metode skal altså hente alle rækkerne, fra vores database, mens næste metode skal hente de rækker som matcher den søgestreng vi giver fra vores GUI. Den tredje metode skal bruges til at indsætte en ny række, med vores owner (og 'dog' selvfølgelig). Lad os gå igang med at lave koden for de to første metoder, da det er disse der er lettest at lave her og nu. Den første metode skulle bare hente alle rækker, og herefter returnere en array liste med de owners den får ud af databasen. Til denne operation kan du bruge følgende SQL forespørgsel - SELECT owner_id, owner_name, owner_age, owner_address, dog_name FROM db_owners JOIN db_dogs ON db_owners.owner_id = db_dogs.dog_owner ORDER BY owner_id ASC, og vores DogBaseMapper klasse ser nu således ud
Fold kodeboks ind/udJava kode 

Vi kunne allerede nu lave koden til de to næste metoder, men lad os hellere gå tilbage til vores GUI og lave den event, som skal bruge vores getAllRows() metode. Husk at før vi går i gang med denne kode at inkluderer MySQL J Connectoren til dit projekts CLASSPATH, samt selvfølgelig lige tjekke at din MySQL database stadig væk kører (Gør den formentlig med mindre du har slukket computeren i mellemtiden).

Den knap der skal bruge denne metode, er vores Browse All Entries knap. Da vi allerede har klargjort den med en action listener, samt en metode der kaldes hver gang man klikker på knappen, så kan vi begynde at skrive kode i denne metode. Men først skal vi lige have nogle flere objekt variabler til vores GUI klasse. F.eks. skal vi bruge en variabel, der kan fortælle os vores nuverende indeks, altså hvilken a rækkerne vi kigger på i øjeblikket. Vi skal også bruge en objekt varaibel, til at indeholde alle rækkerne, som vi får ud af vores ArrayList, så vi ikke behøver at kalde getAllRows() metoden hver eneste gang. Vores GUI klasse ser nu således ud (Bemærk at jeg endnu ikke har lavet koden til metoden browseAllEntriesBtnActionPerformed())
Fold kodeboks ind/udJava kode 

Vi kan nu begynde på vores browseAllEntriesBtnActionPerformed() metode. Metoden skal altså hente alle vores rækker, med metoden getAllRows(), og herefter vise den første række, som vi har fået ind i vores liste. Bemlrk at jeg har valgt at vise kun metoden i stedet for hele klassen, da jeg lige nu kun skal lave ændringer i denne (Bortset fra imports af f.eks. FileNotFoundException, IOException og SQLException).
Fold kodeboks ind/udJava kode 

Koden er forholdsvis simpel, og tager alle rækkerne fra vores database, og lægger disse ind i vores liste kaldet entries. Vi tager nu det første element i denne liste og viser dens indhold i nogle tekst felter, samt viser tekst i vores navigations tekst felter. Vi sætter bl.a. her også previous og next buttons som enabled, så vi nu kan trykke på dem. Bemærk at vi her catcher vores exceptions, men indtil videre sker der ikke noget når der sker en exception. Senere skal vi vise nogle fejl meddelelser til brugeren, men dette er først til allersidst i denne artikel.

Lad os nu lave metoderne der kaldes når brugeren klikker på henholdsvis next og previous knapperne.
Fold kodeboks ind/udJava kode 

Hvis du kører vores projekt nu, så skulle du gerne kunne trykke på browse all entries knappen, som herefter vil fylde din applikation ud med alle rækkerne. Vi kan nu bruge previous og next knapperne til at gå frem og tilbage imellem hver entry. Nu mangler vi bare at lave de to sidste events, men før dette kan lade sig gøre, så skal vi lige tilbage til vores data mapper og lave koden til disse to. Lad os starte ud med koden der skal returnere de entries som der blev søgt på. Lad os se hvordan denne kode ser ud
Fold kodeboks ind/udJava kode 

Og herefter skal vi lave vores event, der benytter denne metode, med en søgestreng.
Fold kodeboks ind/udJava kode 

Nu mangler vi bare at lave eventen og mapper metoden til at indsætte data til databasen. Ligesom før kan vi benytte noget af den viden vi allerede har erhvervet os om hvordan man indsætter data til databasen.
Fold kodeboks ind/udJava kode 

Vi laver gså her en transaktion, for at sikre at alt vores data er indsat korrekt, før vi laver en commit. Vi kan nu lave vores event metode, som indsætter til databasen, og bagefter kalder browseAllEntriesBtnActionPerformed() metoden, for at opdatere denne.
Fold kodeboks ind/udJava kode 

Vores applikation er faktisk færdig nu, men du kan evt. i alle vores catch statements lave en JOptionPane. En JOptionPane, er et auto generet dialog vindue, hvor du nemt kan vise f.eks. fejl meddelelser til brugeren. Du kan altså skrive således - JOptionPane.showMessage(this, "Der skete en fejl!"); - og den vil så lave et nyt dialog vindue, hvor der vil stå der skete en fejl!. Du kan selvfølgelig bare ændrer teksten til det du syntes. Her ser du hele vores GUI kode, hvis du skulle få brug for dette.
Fold kodeboks ind/udJava kode 


Opsummering


I denne artikel fik vi lært hvordan man opsætter en simpel MySQL database, og får denne til at 'lege' sammen med Java. Brug af databaser er meget brugt, når man skal gemme meget data, til brug i ens program. Der findes mange andre databaser end MySQL, men da denne er gratis og samtidig meget populær (der findes meget materiale om denne på nettet), så var det den jeg valgte. Du har lært at oprette en forbindelse til databasen, og ved at der skal bruges en driver for at dette er muligt. Til MySQL kan man downloade en såkaldt J Connector, fra MySQL's hjemmeside, som man så kan inkluderer i ens java programs CLASSPATH. Vi har også set på hvordan med SQL statements kan hente data fra databasen og nemt vise disse i ens java program. Desuden har vi arbejdet med database transaktioner, da vi skulle indsætte data til databasen med java. En transaktion er en gruppe af SQL forespørgsler til databasen, som kun gemmes permanent, hvis disse bliver committed. På den måde kan vi få flere SQL forespørgsler til at arbejde som en, og derved sikre at vi ikke får data i databsen der har løse ender. Til at indsætte data til databasen brugte vi det man kalder et prepared statement, som er et statement, hvor vi kan give nogle variable værdier til. Et prepared statements, SQL har nogle spørgsmålstegn, som fungerer som indikatorer for hvor vi ønsker at have noget variabel data. Vi kan så henholdsvis kalde metoder som setString() og setInt(), samt andre ud fra den datatype der skal bruges, hvor vi nemt kan indsætte den variable værdi til vores prepared statement. En af de vigtige ting vi også lærte var at vi altid skal huske at lukke en forbindelse til databasen, efter brug. Dette er for at sikre at vi ikke har en forbindelse der ligger og kører uden at blive brugt, og derved opsluger computer memory. Det er også en god skik/praksis at lukke statements/resultsets efter brug, da disse også kan ligge og rode uden at blive brugt. Java har dog noget der hedder Garbage Collection, som skal sørge for at indsamle dette garbage, men man ved aldrig helt hvornår den kører, eller om den får det hele med (Du kan dog kalde denne eksplicit hvis du ønsker dette), men også hvis du pludselig skal skifte over og programmerer i f.eks. sproget c++, så findes der ikke en garbage collector, og her er det ekstra vigtigt at du husker at lukke disse ting efter brug.
Vi har også set på hvordan man kan opbygge et forholdsvist simpelt GUI, som kan samarbejde med vores database. Der er selvfølgelig mange måder at gøre det på, men her har jeg i hvertfald vist en måde at gøre det på.

Har du ikke fået nok af Java programmering, men tværtimod har fået mod på at lære noget mere, så glæd dig til den næste artikel i serien!

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (6)

User
Theis @ 01.03.12 23:18
Efter en kort skimtning. Tja. Altså JDBC på den måde du bruger er fint nok til meget små programmer. Men det er ikke anbefalelsesværdigt at bruge JDBC uden et ekstra mapping lag.

Forestil dig hvis du ønsker at holde en many-to-many relation i din database synkroniseret med en many-to-many relation som objecter i java. Det er et helvede er gøre på den måde der nævnes.

Hvis folk ønsker at lave mere avancerede kontakt til databaser vil jeg anbefale at folk benytter sig af Hibernate: http://www.hibernate.org/
User
Martin Rohwedder @ 01.03.12 23:57
Er enig Theis, formålet var at give en forståelse af hvordan man i praksis kan benytte java sammen med en database.

Kan i øvrigt også anbefale folk at kigge mere på hibernate, hvis de ønsker at arbejde videre med java og databaser.
User
Jian @ 09.07.12 02:45
Hej Martin,

når jeg køre main klasse får jeg en fejl?

File Not Found Exception
DBConnection.properties (The system cannot find the file specified)

kan du fortælle mig hvad jeg har gjordt forkert her? :D
User
Martin Rohwedder @ 09.07.12 18:06
Har du lavet filen DBConnection.properties, og har du lagt den i samme mappe som din Main klasse?
User
Mikkel @ 26.03.13 14:35
Martin og Theis, kunne i forklare lidt mere om det der? Jeg skal til at lave 2. sem. projekt, og vi er indtil videre kun blevet introduceret til JDBC driveren. Men vi skal netop lave flere mange til mange relationer. Hvor er forskellen på JDBC og hibernate?
User
Martin Rohwedder @ 26.03.13 17:51
JDBC er javas simple svar på hvordan du kan lave forespørgsler til en database og få nogle rækker tilbage til Java. Hibernate er derimod et framework, som samtidig hjælper med at mappe de ting du henter fra databasen til entiteter, som du kan bruge ligesom almindelige objekter.

Du kan evt. læse meget mere om hibernate på denne hjemmeside - http://hibernate.org/
Du skal være logget ind for at skrive en kommentar.
t