JDBC

Opgaver
Der forudsættes at man har et grundlæggende kendskab til SQL.
Eksemplerne i dette kapitel bygger på PersonDB-eksemplet fra kapitlet om SQL.
Der optræder mange (mulige) exceptions i forbindelse med JDBC, og det forudsættes derfor også at man kan anvende try-catch.
java.sql JDBC står for Java Database Connectivity, og er Java's bud på database-adgang fra et program. For at anvende JDBC skal man importere klasser fra den package der hedder: java.sql
Ikke embedded Som package-navnet antyder, bygger JDBC på anvendelse af SQL. Anvendelsen af SQL i JDBC er parametriseret, og der er derfor ikke tale om embedded database-anvendelse i selve Java-sproget. Alle SQL-sætninger optræder som tekststrenge, der sendes med som parametre i diverse metode-kald.
0. JDBC-driver
Placeres et permanent sted For at kunne kommunikere med en database fra Java, skal man bruge en JDBC-driver. Denne fås som Microsoft SQL Server JDBC Driver 3.0. Man downloader: "sqljdbc_3.0.1301.101_enu.exe", der er en selvudpakkende zip-file. Denne udpakker man (ved at udføre den) til et sted, hvor indholdet kan ligge permanent. Grunden til at indholdet bør placeres et sted, man ikke senere finder anledning til at flytte det fra, er at de projekter vi laver i Eclipse vil referere til en jar-file, der befinder sig i udpakkede. Man kan naturligvis også vælge kun at placere jar-filen (der omtales i det følgende) et permanent sted. Jeg har placeret indholdet i: "C:\­Users\­fkj\­Java\­Microsoft SQL Server JDBC Driver 3.0".
To filer Af det udpakkede er der faktisk kun to filer vi skal bruge, men de øvrige filer indeholder dokumentation etc., der evt. kan være nyttigt.
0.1 Windows Authentication
Den ene file Den ene file vi skal bruge, gør det muligt at logge ind på SQL Server med ens Windows Credentials — dvs. uden at man skal angive brugernavn og password i forhold til database serveren. Det er langt det nemmeste, og vi vil derfor bruge med mindre andet er påkrævet. Filen filer man som: "Microsoft SQL Server JDBC Driver 3.0\­sqljdbc_3.0\­enu\­auth\­x64\­sqljdbc_auth.dll", eller som: "...\­x86\­sqljdbc_auth.dll", hvis man kører 32 bit. Filen skal under alle omstændigheder kopieres ind i: "C:\­Windows\­System32\" (bemærk, at dette kræver administrator rettigheder, på de nyere versioner af Windows).
0.2 JDBC driveren
Den anden file Den anden file er selve driveren. Den består af filen: "Microsoft SQL Server JDBC Driver 3.0\­sqljdbc_3.0\­enu\­sqljdbc4.jar". Samme sted finder man: "sqljdbc.jar", der skal anvendes hvis man bruger Java 5 (aka 1.5) eller tidligere (det gør man næppe!).
Når har oprettet et projekt i Eclipse, man ønsker skal kunne anvende JDBC-driveren, og dermed kunne tilgå database serveren (i.e. SQL Server), skal man vælge: Project > Properties > Java Build Path > Libraries > Add External JARs... . Dernæst browser man frem til nævnte file: "sqljdbc4.jar", og føjer den til.
1. Database-forbindelse
Når man skal arbejde med JDBC, skal man først og fremmest have en forbindelse til databasen. Denne opnås ved at kalde getConnection-metoden på DriverManager'en, med angivelse af en connection string. En connection string er en (noget primitiv) måde, at angive en række oplysninger, der skal bruges for at oprette forbindelsen.
Hvis vi f.eks. skal oprette forbindelse til PersonDB-databasen, kan det gøres med:
Source 1:
Oprette forbindelse til database
String connectionString =
  "jdbc:sqlserver://localhost:1433;" +
  "instance=SQLEXPRESS;" +
  "databaseName=PersonDB;" +
  "integratedSecurity=true;";

Connection connection = DriverManager.getConnection( connectionString );
localhost henviser til at SQL serveren kører på den samme maskine som vores Java-program (hvis den er på en anden maskine skal man angive host navnet på denne. F.eks. "database.fkj.dk" (eksisterer ikke), eller et "IP-nummer").
1433 er port-nummeret på serveren (meget udbredt valg at placere serveren på denne port, og det gør det nemmere at udveksle program-eksempler etc.).
Da vi anvender en Express udgave af SQL Server skal vi anføre: "instance=SQLEXPRESS". Brugte vi i stedet den rigtige SQL Server, skulle denne angivelse helt udelades.
Den sidste angivelse med: "integratedSecurity=true", angiver at vi bruger vores Windoes Credetials til at logge på serveren.
Det returnerede Connection-objekt repræsenterer forbindelsen til databasen, som vi vil anvende i det følgende. Hvis man vil lukke forbindelsen gøres dette med: connection.close(). Forbindelsen lukkes automatisk når programmet terminerer.
2. Queries
For at kunne udføre en query skal man have et Statement-objekt, der repræsenterer den sætning man vil udføre:
Source 2:
Instantiere Statement-objekt
Statement statement = connection.createStatement();
        
Statement-objektet kan bruges til at udføre queries der laver et data-udtræk, eller queries der ændrer data.
2.1 Data-udtræk
executeQuery Selvom Statement-objektet repræsenterer den SQL-sætning vi udfører, skal den have selve SQL-sætningen som en teksstreng. Det gøres ved at udføre executeQuery- eller executeUpdate-metoderne. I forbindelse med data-udtræk (i.e. SELECT-sætninger) anvendes executeQuery:
 
ResultSet executeQuery( String query );
F.eks. kan vi lave et udtræk fra person-tabellen i PersonDB, med alle personer som bor på Paradisæblevej:
Source 3:
Anvende Statement-objekt til data-udtræk
String query = "SELECT * FROM person WHERE gade = 'Paradisæblevej'";
        
ResultSet resultSet = statement.executeQuery( query );
Metoden returnerer udtrækket i form af et ResultSet-objekt, der kan bruges til at gennemløbe rækkerne. Objektet har en række iterator metoder, hvormed vi kan bevæge os fra række til række, og tilgå attributternes værdier en efter en.
Before first, next Iterator-mæssigt er vi fra starten placere "before first". Det betyder, at der endnu ikke er en aktuel række, og vi derfor skal kalde next-metoden, for at stå ved den første række. next-metoden flytter hver gang frem til næste række, og returnerer om der er en sådan række. Vi kan derfor brug den til at styre en while-løkke, der gennemløber rækkerne:
Source 4:
Skabelon for iteration af data-udtræk
while ( resultSet.next() ) {
  // tilgå attributterne i den enkelte række
}
Idéen med at vi fra starten er placeret "before first", er netop at muliggøre denne form for løkke.
Når vi for hver række tilgår attrbutterne, sker dette ikke ved iteration, men ved eksplicit tilgang. En række type-specifikke metoder tager enten attributtens navn, eller dens position i rækken:
 
boolean getBoolean( int columnIndex )
boolean getBoolean( String columnName )

Date getDate( int columnIndex )
Date getDate( String columnName )

double getDouble( int columnIndex )
double getDouble( String columnName )

int getInt( int columnIndex )
int getInt( String columnName )

String getString( int columnIndex )
String getString( String columnName )
Der findes mange af disse metoder, og ovenstående er kun de mest anvendte.
Vi kan f.eks. anvende dem til at aflæse og udskrive attributterne fra udtrækket, fra person-tabellen, i Source 3:
Source 5:
Iteration af data-udtræk, med aflæsning af attributter
while ( resultSet.next() ) {
  int id = resultSet.getInt( "id" );
  String navn = resultSet.getString( "navn" );
  String gade = resultSet.getString( "gade" );
  int nr = resultSet.getInt( "nr" );
  int postNummer = resultSet.getInt( "post_nummer" );
  int alder = resultSet.getInt( "alder" );
  
  System.out.println( id + ", " + navn + ", " + gade +
                      ", " + nr + ", " + postNummer +
                      ", " + alder );
}
1, Anders And, Paradisæblevej, 3, 7430, 32
2, Rip And, Paradisæblevej, 3, 7430, 10
3, Rap And, Paradisæblevej, 3, 7430, 10
4, Rup And, Paradisæblevej, 3, 7430, 10
Vi har her valgt at placere attributternes værdier i lokale variable, for at understrege at get-metoderne returnerer de ønskede typer.
2.2 Queries der ændrer
executeUpdate Ønsker vi at ændre i databasen (i.e. INSERT, UPDATE eller DELETE), kan vi ikke anvende executeQuery-metoden, da dette ikke giver noget data-udtræk (i.e. ResultSet). I stedet skal vi bruge executeUpdate-metoden, der på samme måde som executeQuery tager en SQL-sætning som parameter, men i stedet returnerer hvor mange rækker der er påvirket af udførelsen. (Vi siger her "påvirket", og ikke "ændret", da f.eks. en UPDATE-sætning, der bekræfter værdierne i en række, ikke ændrer dem, men stadig påvirker dem; hvorfor metoden i et sådant tilfælde vil returnere 1, selvom intet er ændret!).
 
int executeUpdate( String query );
  [Ikke skrevet færdig]
3. Model-klasser
JDBC repræsenterer den helt basale adgang til en database, og arbejdet med JDBC er derfor primitivt set med objektorienterede øjne.
  [Ikke skrevet færdig]