Saying Hello
Introductie
Gegevens Opvragen
Gegevens Aanpassen
Geavanceerde Onderwerpen
Los gekoppelde systemen
Eind Opdracht:Sweet Kitty Shop
Saying Goodbye

Data Toegang : OOP Style

Tot nog toe hebben we geleerd om om te gaan met verschillende manieren om een databank te benaderen en resultaten te bekomen. Deze methoden hebben we neergeschreven in een object genaamd BeerData.

Dit object vervult op dit moment eigenlijk een belangrijke taak. Deze zorgt er namelijk voor dat Bieren kunnen worden opgevraagd uit een databank. Om de effectieve connectie en opvraging te doen, maakt deze gebruik van de JdbcFacade, van wie de kerntaak het beheren van de JDBC connectie is.

Omdat dit soort objecten, die het bundelen van de database aanvragen beheren, veel voorkomen. Wordt dit vaak ook het Data Acces Object genoemd. Dit is een object dat als kerntaak het beheren en coördineren van de aanvragen voor een specifieke Data object bijhoudt. [BeerData]

Een Data Object is dan weer een object die als kerntaak het beheren van de gegevens van een specifiek, samenhorend, geheeld heeft. Tot nog toe hebben we hier geen objecten voor gemaakt, maar gebruik gemaakt van losse variabelen die niet leken samen te horen.

Dit is dus de laatste laag abstractie die we moeten leggen. Op deze manier houden we rekening met verandering. Als er ergens in het systeem iets veranderd, heeft dat enkel invloed op dat onderdeel, en niet op de overige delen van de code.

Om dit nu tewerk te stellingen moeten we wat code gaan omvormen en klassen bij ontwikkelen.


In eerste instantie hebben we een structurele beslissing te nemen. We moeten namelijk bepalen of een package by feature of package by Layer structuur gaan hanteren. Even kort een wie is wat.

Package by Layer. Hierbij worden alle objecten die eenzelfde soort kerntaak hebben, samen gevoegd. Zo zal er een pakket zijn daos, waarin alle Dao’s aanwezig zullen zijn van een project. In ons geval bijvoorbeeld BeerDao, BrewerDao, CategoryDao, … Eveneens zullen alle Data Objecten bij elkaar geplaatst worden (Beer, Brewer, Category), alle Services bij elkaar, alle exceptions bij elkaar, …

Package by Feature. Hierbij worden bij elkaar horende features bij elkaar geplaatst en onderverdeeld. Zo zal alles wat met een Beer te maken heeft. (De Dao, Service, Data Object, Controller, …) bij elkaar geplaatst worden. Op deze manier zitten alle features bij elkaar en van elkaar gescheiden.

Welke jij liever gebruikt laten we in het midden, maar in het verdere verloop van deze cursus is gekozen om te werken met de Package by Feature structuur. Deze biedt een aantal voordelen.

  • Functionaliteiten zijn bij elkaar terug te vinden, geen nodeloze navigatie tussen verschillende pakketten voor kleine aanpassingen
  • Verwijdering van een feature is snel en eenvoudig omdat alles samen geplaatst is
  • Makkelijke manier van systemen terug te vinden.

Meer informatie rond dit onderwerp kan je hier vinden.


Nu kunnen we onze opbouw maken. Eerst en vooral hebben we een Beer klasse nodig, zodat we Beer objecten kunnen aanmaken. Dit plaats we in het pakket beers onder het pakket beerapp.

Deze klasse heeft 5 velden met voor elk een setter en getters. Alsook een standaard constructor, equals, hashcode en toString methode. We maken dit object ook onmiddellijk Serializeerbaar zodat we aan Pessimistic Locking kunnen doen in de Databank.

package be.learningfever.jdbclessons.beersapp.beers;

import java.io.Serializable;
import java.util.Objects;

public class Beer implements Serializable {
    private int id;
    private String name;
    private double price;
    private double alcohol;
    private int stock;

    public Beer() {

    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public double getAlcohol() {
        return alcohol;
    }

    public void setAlcohol(double alcohol) {
        this.alcohol = alcohol;
    }

    public int getStock() {
        return stock;
    }

    public void setStock(int stock) {
        this.stock = stock;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Beer beer = (Beer) o;
        return getId() == beer.getId() &&
                Double.compare(beer.getPrice(), getPrice()) == 0 &&
                Double.compare(beer.getAlcohol(), getAlcohol()) == 0 &&
                getStock() == beer.getStock() &&
                Objects.equals(getName(), beer.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName(), getPrice(), getAlcohol(), getStock());
    }

    @Override
    public String toString() {
        return String.format(
                "%d - %s : %.2f euro with %.2f Alc.%n%d in stock%n",
                getId(),
                getName(),
                getPrice(),
                getAlcohol(),
                getStock()
        );
    }
}

Nu is het tijd om onze Dao te voorzien. Hierbij gaan we rekening houden met de mogelijkheid dat de invulling van de opvraging, later altijd nog kan veranderen. Daarom voorzien we eerst een algemene interface die we later in een Service kunnen gebruiken. Door het gebruik van een interface ipv een klasse, is er enkel een contract afgesproken, maar nog geen invulling. Dit kunnen we dus later altijd nog aanpassen, maar de werking van onze Service klasse zal daardoor niet veranderen.

Na de Dao Interface, is het tijd om een implementatie te schrijven. Deze implementatie is eigenlijk het BeerData object dat we tot nog toe hebben opgemaakt. Deze maakt al gebruik van de JdbcFacade, waardoor het Connectie gedrag uitgefilterd is, en deze klasse enkel de aanvragen van een Beer object doet naar een databank. Deze klasse implementeert dan de BeerDao interface en is een standaard implementatie van die interface.

We kunnen nu dus de interface aanmaken BeerDao in het pakket beers, die ondermeer volgende methoden bevat

  • getBeerById(int id) : Beer
  • updateBeer(Beer) : void

En een implementatie StandardBeerDao, die de interface BeerDao implementeert en zich in het pakket beers > daos bevindt. Deze bevat dan een lege constructor, een constructor die de JdbcFacade aanneemt als parameter, en een getetter en setter voor het JdbcFacade veld. Eveneens zal die een invulling voorzien voor de methoden uit de interface.

In sommige gevallen kan deze Implementatie meer dan dat de interface voorschrijft. Maar onthou dat enkel de methoden van de interface toegankelijk zullen zijn in de hoofd applicatie, waar het de Interface is die vermeld zal worden en niet de concrete klasse zelf.


Als laatste kunnen we een BeerException schrijven die de uitzonderingen van de bieren onderhoudt. Deze kan zich dan in het pakket beers > exceptions bevinden en kan ook de SQLException opvangen.


Op deze manier is er een volledige loskoppeling van alle verschillende kerntaken die de klassen moeten vervolmaken. En verandering is telkens uit de klassen gefilterd waar het enkel als configuratie dient.