1.4-Software-Development-Principles

Storing data with JDBC

Inleiding

Aangezien we aan het leren zijn om data op te slaan, kunen we net zo goed gebruik maken van een daadwerkelijke database. Je hebt geleerd om een database te bevragen met behulpt van SQL. De applicatie die je gebruikt om, in de rol van database ontwerper, queries te uit te voeren heet meestal “de administrator”. (pgAdmin, phpMyAdmin, etc.) Zulke applicaties zijn vaak stand-alone of leven in een browser. De daadwerkelijke database draait in een apart proces, of zelfs op een andere machine.

In plaats van queries runnen vanuit de administrator, leert dit theorie segment je hoe je vanuit een Java applicatie, zulke queries direct naar de server kunnen sturen.

Merk op dat je gebruikelijk mee kunt programmeren. In dit geval gebruik ik een verbinding naar een database die jij misschien niet hebt op jouw systeem. Dus je zult je eigen queries moeten ontwerpen voor jouw database.

Stap 1: Verbinden met de database

Om te communiceren met de daadwerkelijke database server gebruiken we een Connection. We gaan een verbinding opzetten met een postgres systeem, maar een andere database werkt ongeveer hetzelfde.

Installeer de progress bibliotheek

Na de installatie kun je de connectie naar postgres zo configureren:

String url = "jdbc:postgresql://localhost:5432/test?user=adm&password=adm&ssl=true";
Connection conn = DriverManager.getConnection(url);

De meeste complexiteit zit in de connectie url:

Om te verbinden met een mysql Database op mijn server thuis, zou de url er ongeveer zo uit zien:

String url = "jdbc:mysql://192.168.1.1:3306/energy?user=frederik&password=Secret1234&ssl=true";
Connection conn = DriverManager.getConnection(url);

Voor veilidheidsdoeleinden kun je de username/wachtwoord combinatie ook apart meegeven:

String url = "jdbc:mysql://192.168.1.1:3306/energy";
Connection conn = DriverManager.getConnection(url, "frederik", "Secret1234");

Note: Wanneer je klaar bent met je verbinding is het netjes om hem ook weer af te sluiten:

conn.close();

Stap 2: Stuur een query

Versturen van een query gebeurt in drie stappen.

  1. De query klaar zetten (prepare)
  2. Invullen van daadwerkelijke waarden
  3. Versturen van de query (execute)
String query = "SELECT * FROM students WHERE number=?";
PreparedStatement statement = conn.prepareStatement(query);

Merk op dat de query een vraagteken. Deze plek markeert de locatie waar je een waarde kunt invullen in stap twee:

statement.setInteger(1, 123456);

Note: In dit beval binden we een integer waarde aan de eeste parameter in de query. We tellen vanaf 1, niet 0.

Voor tekst waarden hadden we het volgende kunnen doen:

stamement.setString(1, "Hello");

Dit is het enige verschil, de tekst van de query zonder de aanhalingstekens blijft hetzelfde!

Uiteindelijk kun je de query uitvoeren, met de ingevulde data:

ResultSet resultSet = statement.execute();

Wanneer je vergeet om een parameter te verbinden aan een waarde, wordt je door het systeem een mooie exceptie aangeboden.

Note: De reden om dit in drie stappen te doen, in plaats van de waarden gewoon op te nemen in de query tekst, is de volgende: Deze methode voorkomt iedere vorm van SQL injection. De geprepareerde query wordt geanalyseerd als SQL, de data wordt NOOIT gezien als SQL. Dus deze situatie kan niet voorkomen omdat “bobby tables” als data wordt gezien, niet als instructies.

Stap 3: Het resultaat lezen

Wanneer jouw query een aantal records oplevert, wat natuurlijk vaak gebeurt met een “SELECT” query, dan kun je het resultaat van de .execute() methode opslaan in een ResultSet variable.

Het lezen van een ResultSet lijkt erg veel op het gebruik van de CSVReader. Dat is hoe deze code werkt:

while (resultSet.next()) {
    String firstName = resultSet.getString("first_name");
    String lastName = resultSet.getString("last_name");
    int age = resultSet.getInteger("age");
    Date date = resultSet.getDate("birthdate");
    int number = resultSet.getInteger("number");
    // Do something with this data...
    Student result = new Student(number, firstName, lastName, age, birthdate);
	// Add to a list or something...
}

Ten slotte

Wanneer je klaar ben met jouw statement, sluit deze dan ook, zodat andere gebruikers de tabel kunnen lezen:

statement.close();

Samenvatting

Om te bevestigen dat er geen fouten zijn, staat hier onder het code segment dat alle data uit mijn persoonlijke test database (MySQL) leest.

Table

package nl.saxion.sdp.exercises.serialisation;

import java.sql.*;

public class Main {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mysql://192.168.1.1:3306/students?user=frederik&password=Frederik&ssl=true";
        Connection conn = DriverManager.getConnection(url);
        
        String query = "SELECT * FROM student WHERE first_name LIKE ?";
        PreparedStatement stmt = conn.prepareStatement(query);
        
        stmt.setString(1, "%er%");
        
        ResultSet result = stmt.executeQuery();
        while (result.next()) {
            int number = result.getInt("number");
            String firstName = result.getString("first_name");
            String lastName = result.getString("last_name");
            Date birthdate = result.getDate("birthdate");
            System.out.println(number+" : "+firstName+" "+lastName+" "+birthdate);
        }
        
        result.close();
        stmt.close();
        conn.close();
    }
}

Het uitvoeren van dit code segment levert de volgende uitvoer op: Database output

Note: Verbinden met de database staat je toe om individuele tabellen te bevragen, of door te joinen meerdere tabellen. De manier waarop deze tabellen met elkaar verbinden door foreign keys is iets wat je zelf moet afhandelen via iedere query. Maar wanneer je sommige tabellen een weerslag hebben in jouw datamodel, bijvoorbeeld studenten die bij een bepaalde groep horen, dan moet je eerst de studenten inladen en dan ieder student toevoegen aan het juiste “klas” object…

De complete klas (met studenten) structuur kan ineens gelezen worden door een bestand te deserialiseren van een JSON structuur. Wanneer je gebruik maakt van een database zul je zelf jouw “StudentGroup” moeten opvullen uit de database.