Skip to content

7. [TD]: Implementierung der Schicht [DAO] von TD mit API und JDBC

Stichworte: relationale Datenbanken, API, JDBC, SQLException.

Werfen wir noch einmal einen Blick auf die Schichtenarchitektur unserer Anwendung:

Die für die Wahl erforderlichen Daten werden in einer Datenbank gespeichert: MySQL [dbelections]

7.1. Support

Der Ordner [support / chap-07] [1] enthält:

  • die Eclipse-Projekte dieses Kapitels [2];
  • das Skript SQL zur Erstellung der Datenbank MySQL, [dbelections], [3];

7.2. Die Datenbank [dbelections]


Aufgabe: Erstellen Sie die Datenbanken MySQL und [dblelections] gemäß der in Abschnitt 6.4.2 beschriebenen Vorgehensweise.


Die Datenbank [dbelections] ist eine Datenbank vom Typ MySQL mit zwei Tabellen:

  

Die Tabelle [conf] enthält die Konfigurationsinformationen zur Wahl:

 
  • [id]: automatisch inkrementierter Primärschlüssel;
  • [version]: Versionsnummer des Datensatzes – kann hier ignoriert werden;
  • [sap]: Anzahl der zu besetzenden Sitze;
  • [seuilelectoral]: der Schwellenwert, unterhalb dessen eine Liste ausgeschlossen wird;

Der Inhalt lautet wie folgt:

 

Die Tabelle [listes] enthält die Kandidatenlisten der Wahl:

 
  • [id]: automatisch inkrementierter Primärschlüssel;
  • [version]: Versionsnummer des Datensatzes – kann hier ignoriert werden;
  • [nom]: Name der Liste;
  • [voix]: Stimme der Liste – ist erst nach Eingabe durch den Benutzer in der Ebene [présentation] bekannt;
  • [sieges]: Anzahl der gewonnenen Sitze – ist erst nach der Berechnung der Ebene [métier] bekannt;
  • [elimine]: 1, wenn die Liste ausgeschieden ist, andernfalls 0 – bekannt erst nach Berechnung der Ebene [métier];

Der Inhalt der Tabelle [listes] lautet wie folgt:

 

Das Skript SQL zur Generierung der Datenbank [dbelections] heißt [dbelections.sql] und befindet sich auf dem Server. Sein Code lautet wie folgt:


-- phpMyAdmin SQL Dump
-- Version 4.0.4
-- http://www.phpmyadmin.net
--
-- Client: localhost
-- Erstellt am: Mi, 11. März 2015 um 12:20 Uhr
-- Serverversion: 5.6.12-log
-- Version von PHP: 5.4.12

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- Datenbank: `dbelections`
--
CREATE DATABASE IF NOT EXISTS `dbelections` DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
USE `dbelections`;

-- --------------------------------------------------------

--
-- Struktur der Tabelle `conf`
--

CREATE TABLE IF NOT EXISTS `conf` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `version` int(11) NOT NULL DEFAULT '1',
  `sap` tinyint(4) NOT NULL,
  `seuilelectoral` double NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=2 ;

--
-- Inhalt der Tabelle `conf`
--

INSERT INTO `conf` (`id`, `version`, `sap`, `seuilelectoral`) VALUES
(1, 1, 6, 0.05);

-- --------------------------------------------------------

--
-- Struktur der Tabelle `listes`
--

CREATE TABLE IF NOT EXISTS `listes` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `version` int(11) NOT NULL DEFAULT '1',
  `nom` varchar(20) COLLATE utf8_swedish_ci NOT NULL,
  `voix` int(11) NOT NULL,
  `sieges` int(11) NOT NULL,
  `elimine` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `nom` (`nom`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=8 ;

--
-- Inhalt der Tabelle `listes`
--

INSERT INTO `listes` (`id`, `version`, `nom`, `voix`, `sieges`, `elimine`) VALUES
(1, 21, 'A', 10, 1, 0),
(2, 22, 'B', 20, 2, 0),
(3, 21, 'C', 30, 3, 0),
(4, 13, 'D', 40, 3, 0),
(5, 17, 'E', 50, 6, 0),
(6, 18, 'F', 60, 1, 0),
(7, 19, 'G', 70, 2, 0);

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

7.3. Das Eclipse-Projekt

Das Eclipse-Projekt der Schicht [DAO] sieht wie folgt aus:

  
  • Das Paket [elections.dao.entities] enthält die Objekte, die von der Schicht [DAO] verarbeitet werden;
  • Das Paket [elections.dao.service] enthält die Implementierung der Schicht [DAO];
  • Das Paket [elections.dao.config] enthält die Konfiguration der Schicht [DAO]
  • Das Paket [elections.dao.junit] enthält eine Testklasse JUnit des Projekts;
  • Das Paket [elections.dao.console] enthält eine ausführbare Testklasse;

7.4. Maven-Projektkonfiguration

 

Die Datei [pom.xml], die das Maven-Projekt konfiguriert, lautet wie folgt:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-dao-jdbc-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <!-- Tomcat JDBC -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>
        <!-- Bibliothek jSON -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot-Protokollierung -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
    </dependencies>

    <!-- Plugins -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>config.AppConfig</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <!-- zur Installation des Projekt-Artefakts im lokalen Maven-Repository -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>

Diese Datei entspricht der in Abschnitt 6.5.1 beschriebenen. Es wurden folgende Änderungen vorgenommen:

  • Zeilen 8–12: Es wurde ein übergeordnetes Maven-Projekt definiert. Das Projekt [spring-boot-starter-parent] (Zeile 10) definiert eine sehr große Anzahl von Abhängigkeiten mit ihren Versionen. Wenn eine davon verwendet wird (Zeilen 19–57), muss die Version nicht angegeben werden, da sie im übergeordneten Maven-Projekt definiert ist;
  • Zeilen 40–51: Für die Testklasse des Projekts erforderliche Abhängigkeiten. Diese Abhängigkeiten tragen das Attribut „[<scope>test</scope>]“, was bedeutet, dass sie nur für die Klassen im Ordner „[src / test / java]“ benötigt werden. Diese Abhängigkeiten werden nicht in das endgültige Projektarchiv aufgenommen;
  • Zeilen 53–56: Die Bibliothek [spring-boot-starter-logging] wird von Spring verwendet, um Logs auf dem Bildschirm auszugeben;
  • Zeilen 14–17: Maven-Konfigurationseigenschaften:
    • Zeile 15: gibt an, dass die Quelldateien in UTF-8 programmiert sind;
    • Zeile 16: gibt an, dass der Compiler der Version 1.8 verwendet werden soll;

7.5. Die Entitäten der [DAO]-Schicht

  
  • [ElectionsConfig] ist das Objektmodell, das einer Zeile der Tabelle [CONF] zugeordnet ist;
  • [ListeElectorale] ist das Objektmodell, das einer Zeile der Tabelle [LISTES] zugeordnet ist;
  • [AbstactEntity] ist die übergeordnete Klasse der beiden vorgenannten Klassen. Sie fasst die Felder von [id, version] zusammen, die beiden Klassen gemeinsam sind;
  • [ElectionsException] ist eine Ausnahmeklasse;

7.5.1. Die Klasse [ElectionsException]

  

Die Klasse [ElectionsException] wurde in Abschnitt 4.3 beschrieben. Hier noch einmal ihr Code:


package ...;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

// Ausnahmeklasse für die Anwendung „Elections“
// Die Ausnahme ist unkontrolliert

public class ElectionsException extends RuntimeException implements Serializable {

    // Seriennummer ID
    private static final long serialVersionUID = 1L;

    // Lokale Felder
    private int code;
    private List<String> erreurs;

    // Konstruktoren
    public ElectionsException() {
        super();
    }

    public ElectionsException(int code, Throwable e) {
        // übergeordnet
        super(e);
        // lokal
        this.code = code;
        this.erreurs = getErreursForException(e);
    }

    public ElectionsException(int code, String message, Throwable e) {
        // übergeordnet
        super(message,e);
        // lokal
        this.code = code;
        this.erreurs = getErreursForException(e);
    }

    public ElectionsException(int code, String message) {
        // übergeordnet
        super(message);
        // lokal
        this.code = code;
        List<String> erreurs = new ArrayList<>();
        erreurs.add(message);
        this.erreurs = erreurs;
    }

    public ElectionsException(int code, List<String> erreurs) {
        // übergeordnet
        super();
        // lokal
        this.code = code;
        this.erreurs = erreurs;
    }

    // Liste der Fehlermeldungen einer Ausnahme
    private List<String> getErreursForException(Throwable th) {
        // Die Liste der Fehlermeldungen der Ausnahme wird abgerufen
        Throwable cause = th;
        List<String> erreurs = new ArrayList<>();
        while (cause != null) {
            // Die Meldung wird nur abgerufen, wenn sie != null und nicht leer ist
            String message = cause.getMessage();
            if (message != null) {
                message = message.trim();
                if (message.length() != 0) {
                    erreurs.add(message);
                }
            }
            // nächste Ursache
            cause = cause.getCause();
        }
        return erreurs;
    }

    // Getter und Setter
...
}

Ein Objekt [ElectionsException] wird durch zwei Angaben charakterisiert:

  • Zeile 16: einen Fehlercode;
  • Zeile 17: eine Liste von Fehlermeldungen, die mit dem Stapel der aufgetretenen Ausnahmen verknüpft sind;
  • Die Klasse verfügt über 5 Konstruktoren (Zeilen 20, 24, 32, 40, 50);
  • Zeilen 59–76: Mit der Methode [getErreursForException] können die Fehlermeldungen aus dem Ausnahmestapel abgerufen werden;

7.5.2. Die Klasse [AbstractEntity]

  

Die Klasse [AbstractEntity] sieht wie folgt aus:


package elections.dao.entities;

import java.io.Serializable;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public abstract class AbstractEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    // Felder
    protected Long id;
    protected Long version;

    // Konstruktoren
    public AbstractEntity() {

    }

    public AbstractEntity(Long id, Long version) {
        this.id = id;
        this.version = version;
    }

    // Signatur
    public String toString() {
        try {
            return new ObjectMapper().writeValueAsString(this);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    // Getter und Setter
...
}

Sie speichert die Felder [ID, NOM] der Zeilen der Tabellen [CONF] und [LISTES] (Zeilen 8–9).

  • Zeile 8: Die Klasse verfügt über das Attribut [Abstract], das angibt, dass sie nicht instanziiert werden kann. Sie kann nur abgeleitet werden;
  • Zeilen 26–33: Signatur jSON des Objekts;
  • Zeile 28: Es wird die Zeichenkette jSON von [this] zurückgegeben. Wenn [this] zum Zeitpunkt der Ausführung ein von [AbstactEntity] abgeleitetes Objekt darstellt, wird die Zeichenfolge jSON des abgeleiteten Objekts zurückgegeben. Abgeleitete Klassen müssen daher keine Methode [toString] definieren. Die Methode der übergeordneten Klasse reicht aus;

7.5.3. Die Klasse [ElectionsConfig]

  

Die Klasse [ElectionsConfig] lautet wie folgt:


package elections.dao.entities;


public class ElectionsConfig extends AbstractEntity {

    private static final long serialVersionUID = 1L;
    // Felder
    private int nbSiegesAPourvoir;
    private double seuilElectoral;

    // Konstruktoren
    public ElectionsConfig() {
    }

    public ElectionsConfig(Long id, Long version, int nbSiegesAPourvoir, double seuilElectoral) {
        // übergeordnetes Element
        super(id, version);
        // lokale Felder
        this.nbSiegesAPourvoir = nbSiegesAPourvoir;
        this.seuilElectoral = seuilElectoral;
    }

    // Getter und Setter
...
}
  • Zeile 4: Die Klasse erweitert die Klasse [AbstractEntity];
  • Zeilen 8–9: Speichern die Informationen der Spalten [SAP, SEUILELECTORAL] aus der Tabelle [CONF];

7.5.4. Die Klasse [ListeElectorale]

  

Die Klasse [ListeElectorale] lautet wie folgt:


package elections.dao.entities;

public class ListeElectorale extends AbstractEntity {

    // Felder
    private String nom;
    private int voix;
    private int sieges;
    private boolean elimine;

    // Konstruktoren
    public ListeElectorale() {
    }

    public ListeElectorale(String nom, int voix, int sieges, boolean elimine) {
        // übergeordnet
        super();
        // lokale Felder
        initNom(nom);
        initVoix(voix);
        initSieges(sieges);
        this.elimine=elimine;
    }

    public ListeElectorale(Long id, Long version, String nom, int voix, int sieges, boolean elimine) {
        // übergeordnetes Objekt
        super(id, version);
        // lokale Felder
        initNom(nom);
        initVoix(voix);
        initSieges(sieges);
        this.elimine=elimine;
    }

    // private Methoden
    private void initNom(String nom) {
        this.nom = nom.trim();
        if ("".equals(nom)) {
            throw new ElectionsException(10, "Le nom ne peut être vide");
        }
    }

    private void initVoix(int voix) {
        this.voix = voix;
        if (voix < 0) {
            throw new ElectionsException(11, "Le nombre de voix ne peut être <0");
        }
    }

    private void initSieges(int sieges) {
        this.sieges = sieges;
        if (sieges < 0) {
            throw new ElectionsException(12, "Le nombre de sièges ne peut être <0");
        }
    }

    // Getter und Setter

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        initNom(nom);
    }

    public int getVoix() {
        return voix;
    }

    public void setVoix(int voix) {
        initVoix(voix);
    }

    public int getSieges() {
        return sieges;
    }

    public void setSieges(int sieges) {
        initSieges(sieges);
    }

    public boolean isElimine() {
        return elimine;
    }

    public void setElimine(boolean elimine) {
        this.elimine = elimine;
    }

}
  • Zeile 3: Die Klasse erweitert die Klasse [AbstractEntity];
  • Zeilen 6–9: Die Klasse speichert die Spalten [NOM, VOIX, SIEGES, ELIMINE] der Tabelle [LISTES];

7.6. Spring-Konfiguration der Schicht [DAO]

 

Die Klasse [AppConfig] ist eine Spring-Konfigurationsklasse, die den Datenbankzugriff wie folgt konfiguriert:


package elections.dao.config;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan(basePackages = { "elections.dao.service" })
@EnableCaching
public class AppConfig {

    // Konstanten
    public final static String URL = "jdbc:mysql://localhost:3306/dbelections";
    public final static String USER = "root";
    public final static String PASSWD = "";
    public final static String DRIVER_CLASSNAME = "com.mysql.jdbc.Driver";
    public final static String SELECT_LISTES = "SELECT ID, VERSION, NOM, VOIX, SIEGES, ELIMINE FROM LISTES";
    public final static String SELECT_CONF = "SELECT ID, VERSION, SAP, SEUILELECTORAL, SAP FROM CONF";
    public final static String UPDATE_LISTES = "UPDATE LISTES SET VOIX=?, SIEGES=?, ELIMINE=? WHERE ID=?";

    @Bean
    public DataSource dataSource() {
        // Datenquelle TomcatJdbc
        DataSource dataSource = new DataSource();
        // Zugriffskonfiguration JDBC
        dataSource.setDriverClassName(DRIVER_CLASSNAME);
        dataSource.setUsername(USER);
        dataSource.setPassword(PASSWD);
        dataSource.setUrl(URL);
        // eine ursprünglich geöffnete Verbindung
        dataSource.setInitialSize(1);
        // Ergebnis
        return dataSource;
    }

    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("electionsConfig");
    }
}
  • Zeilen 25–38: Der Zugriff auf BD erfolgt über eine Datenquelle [tomcat-jdbc]. Dieser Datenquellentyp wurde in Abschnitt 6.5 verwendet und erläutert;
  • Zeilen 17–23: Eine Reihe statischer Konstanten, auf die alle Klassen des Projekts zugreifen können;
  • Zeile 13: Die Annotation [@Configuration] macht die Klasse [AppConfig] zu einer Spring-Konfigurationsklasse;
  • Zeile 11: Die Annotation [@ComponentScan] gibt die Pakete an, in denen Spring-Objekte zu finden sind. Hier definieren wir ein Spring-Objekt im Paket [dao]. Die Annotation [@ComponentScan] macht die Klasse zu einer Konfigurationsklasse, wodurch wir uns das Setzen der Annotation [@Configuration] sparen;
  • Zeile 12 aktiviert die Cache-Verwaltung. Das Prinzip ist wie folgt:
    • Man fügt die Annotation [@Cacheable('nom_du_cache')] zu einer Methode M hinzu. Der „Cache-Name“ ist der in Zeile 41 verwendete Name;
    • Wenn die Methode M zum ersten Mal aufgerufen wird, werden ihre Ergebnisse zurückgegeben und gleichzeitig in den durch die Annotation benannten Cache gespeichert;
    • wird die Methode M bei den folgenden Aufrufen mit denselben Parametern wie beim ersten Mal nicht erneut ausgeführt, sondern Spring gibt lediglich die zwischengespeicherten Werte zurück;

7.7. Konfiguration der Protokolle

Die Logging-Bibliotheken werden durch die folgende Abhängigkeit in [pom.xml] definiert:


        <!-- Spring Boot-Protokollierung -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Diese Abhängigkeit führt zu folgenden Bibliotheken:

  

Die Protokollierung wird von der Bibliothek [logback] übernommen. Sie wird über zwei Dateien konfiguriert:

  • [logback.xml] für den Hauptzweig des Codes;
  • [logback-test.xml] für den Testzweig des Codes. Fehlt diese Datei, wird die vorherige Datei verwendet;

Diese beiden Dateien müssen sich im Verzeichnis [Classpath] des Projekts befinden. Aus diesem Grund werden sie im folgenden Ordner abgelegt:

  • [src / main / ressources] für den Hauptzweig des Codes;
  • [src / test / ressources] für den Testzweig des Codes;

Der Inhalt der Dateien ist hier identisch:


<configuration> 

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- Encodern wird standardmäßig der Typ zugewiesen
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- Steuerung der Protokollierungsstufe -->
  <root level="info"> <!-- Info, Debug, Warn -->
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Entscheidend ist Zeile 12, in der die gewünschte Detailebene festgelegt wird:

  • [debug]: die detaillierteste Stufe;
  • [off]: keine Protokolle;
  • [info]: normale Protokollstufe;
  • [warn]: wie Stufe [info] plus Warnmeldungen. Diese Meldungen weisen auf einen möglichen Fehler hin;

Wechseln Sie in den Modus [debug], sobald bei der Ausführung Fehler auftreten.

7.8. Implementierung der Schicht [DAO]

  

Die Schnittstelle [IDao] der Schicht [DAO] lautet wie folgt:


package istia.st.elections.webapi.client.dao;

import istia.st.elections.webapi.client.entities.ElectionsConfig;
import istia.st.elections.webapi.client.entities.ListeElectorale;

public interface IDao {
    // Konfiguration der Wahlen
    public ElectionsConfig getElectionsConfig();

    // die Kandidatenlisten
    public ListeElectorale[] getListesElectorales();

    // Speicherung der Wahlergebnisse
    public void setListesElectorales(ListeElectorale[] listesElectorales);

}

Das Grundgerüst der Klasse [ElectionsDaoJdbc], die die Schicht [dao] mit einer Datenbank implementiert, sieht wie folgt aus:


package elections.dao.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;

@Component
@SuppressWarnings("unused")
public class ElectionDaoJdbc implements IElectionsDao {

    @Autowired
    private DataSource dataSource;

    @Cacheable("electionsConfig")
    // Abruf der Wahlkonfiguration
    public ElectionsConfig getElectionsConfig() {
        throw new RuntimeException("[getElectionsConfig] not yet implemented");
    }

    // Abruf der Listen
    public ListeElectorale[] getListesElectorales() {
        throw new RuntimeException("[getListesElectorales] not yet implemented");
    }

    // Änderung der Listen [voix, sieges, elimine]
    public void setListesElectorales(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[setListesElectorales] not yet implemented");
    }

    // -------------------- private Methoden

    // Verwaltung des „finally“-Blocks
    private ElectionsException doFinally(int code, ResultSet rs, PreparedStatement ps, Connection connexion) {
        // Abschluss ResultSet
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e1) {

            }
        }
        // Schließen [PreparedStatement]
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e2) {

            }
        }
        // Verbindung schließen
        if (connexion != null) {
            try {
                connexion.close();
            } catch (SQLException e3) {
                // Es wird ein [ElectionsException] zurückgegeben
                return new ElectionsException(code, e3);
            }
        }
        // keine Ausnahme
        return null;
    }

    // Fehlerbehandlung
    private ElectionsException doCatchException(int code1, int code2, Connection connexion, Throwable th) {
        // Es wird ein [ElectionsException] generiert
        ElectionsException ex1 = new ElectionsException(code1, th);
        // Transaktion stornieren
        try {
            if (connexion != null) {
                connexion.rollback();
            }
        } catch (SQLException e2) {
        }
        // Die Ausnahme wird zurückgegeben
        return ex1;
    }
}
  • Zeile 24: Die Annotation [@Cacheable] ist eine Spring-Annotation, die anweist, die Ergebnisse der Methode [getElectionsConfig] im Speicher zu halten („zu cachen“). Dies ist hier möglich, da sich der Inhalt der Tabelle [CONF] nie ändert. Es wäre nicht möglich, diese Annotation auf die Methode [getListesElectorales] anzuwenden, da sich der Inhalt der Tabelle [LISTES] im Laufe der Zeit ändert;
  • die Methoden [doCatchException] und [doFinally] geben einen Typ [ElectionsException] zurück. Die Methode [doFinally] gibt einen Zeiger vom Typ null zurück, wenn die Freigabe der Ressourcen fehlerfrei erfolgt ist;

Aufgabe: Schreiben Sie die Klasse [ElectionDaoJdbc] in Anlehnung an die in Abschnitt 6.5.2 behandelte Klasse [IntroJdbc02].


7.9. Die Testklasse [Main]

  

Die Klasse [Main] lautet wie folgt:


package elections.dao.console;

import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;

public class Main {

    // Datenquelle
    private static IElectionsDao dao;

    public static void main(String[] args) {
        // Die Referenz der Schicht [DAO] wird nach der Instanziierung des Spring-Kontexts abgerufen
        try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)) {
            // Abruf der Datenquelle
            dao = ctx.getBean(IElectionsDao.class);
        } catch (Exception e) {
            System.out.println("Les erreurs suivantes se sont produites lors de l'initialisation du contexte Spring -------");
            for (String erreur : getErreursForThrowable(e)) {
                System.out.println(erreur);
            }
            // Ende
            return;
        }
        // Auslesen der Tabelle BD
        ElectionsConfig electionsConfig = null;
        ListeElectorale[] listes;
        try {
            // Inhalt der beiden Tabellen
            electionsConfig = dao.getElectionsConfig();
            listes = dao.getListesElectorales();
        } catch (ElectionsException e) {
            System.out.println("Les erreurs suivantes se sont produites lors de la lecture des tables ----------");
            for (String erreur : e.getErreurs()) {
                System.out.println(erreur);
            }
            // Ende
            return;
        }
        // Alles ist gut gelaufen – Anzeige
        System.out.println(String.format("Nombre de sièges à pourvoir : %d", electionsConfig.getNbSiegesAPourvoir()));
        System.out.println(String.format("Seuil électoral : %5.2f", electionsConfig.getSeuilElectoral()));
        System.out.println("Listes candidates----------------");
        for (ListeElectorale liste : listes) {
            System.out.println(liste);
        }
    }

    // private Methoden ------------------
    private static List<String> getErreursForThrowable(Throwable th) {
        // Die Liste der Fehlermeldungen der Ausnahme wird abgerufen
        Throwable cause = th;
        List<String> erreurs = new ArrayList<>();
        while (cause != null) {
            // Die Meldung wird nur abgerufen, wenn sie != null und nicht leer ist
            String message = cause.getMessage();
            if (message != null) {
                message = message.trim();
                if (message.length() != 0) {
                    erreurs.add(message);
                }
            }
            // nächste Ursache
            cause = cause.getCause();
        }
        return erreurs;
    }

}
  • Zeilen 21–31: Abruf einer Referenz auf die Ebene [DAO];
  • Zeile 21: Es wird eine Syntax namens try_with_resources verwendet. Ihre Syntax lautet wie folgt:

try (T ressource=expression) {
            // Ressourcenauslastung
...
}
  • (Fortsetzung)
    • Der Typ T in Zeile 1 muss die Schnittstelle [java.lang.AutoCloseable] implementieren;
    • Am Ende des Blocks der Zeilen 1–4 wird die Ressource vom Typ [java.lang.AutoCloseable] automatisch freigegeben, unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht. Wer die Sprache C# kennt, wird hier einen syntaktischen und funktionalen Verwandten der using-Klausel (T Ressource = Ausdruck) erkennen;
  • Zeilen 40–49: Verwendung der Schicht [DAO], um den Inhalt der Tabellen [CONF] und [LISTES] abzurufen;
  • Zeilen 41–47: Der Cache [electionsconfig] wird getestet. Dieser wurde an zwei Stellen definiert:
    • in der Klasse [ElectionsDaoJdbc]:

  @Cacheable("electionsConfig")
  public ElectionsConfig getElectionsConfig() {
  • (Fortsetzung)
    • in der Konfigurationsklasse [AppConfig]:

@EnableCaching
public class AppConfig {
...
  @Bean
  public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("electionsConfig");
  }
}
  • Zeile 59: Schließen des Spring-Kontexts;
  • Zeilen 62–67: Anzeige der abgerufenen Informationen;

Die mit einer implementierten Schicht [DAO] erzielten Ergebnisse lauten wie folgt:

...
début requête 1 : 11:09:29:752
fin requête 1 et début requête 2: 11:09:30:132
fin requête 2 : 11:09:30:133
...
Nombre de sièges à pourvoir : 6
Seuil électoral :  0,05
Listes candidates----------------
{"id":1,"version":9,"nom":"A","voix":32000,"sieges":2,"elimine":false}
{"id":2,"version":13,"nom":"B","voix":25000,"sieges":2,"elimine":false}
{"id":3,"version":14,"nom":"C","voix":16000,"sieges":1,"elimine":false}
{"id":4,"version":13,"nom":"D","voix":12000,"sieges":1,"elimine":false}
{"id":5,"version":14,"nom":"E","voix":8000,"sieges":0,"elimine":false}
{"id":6,"version":13,"nom":"F","voix":4500,"sieges":0,"elimine":true}
{"id":7,"version":13,"nom":"G","voix":2500,"sieges":0,"elimine":true}
  • Zeilen 2–4: zeigen den Einfluss des Caches:
    • Anfrage 1 dauert 80 Millisekunden;
    • Abfrage 2 dauert 1 Millisekunde;

Wenn die Datenbank abgeschaltet ist, erhält man folgende Ergebnisse:

1
2
3
4
5
Les erreurs suivantes se sont produites lors de la lecture des tables ----------
Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
Connection refused: connect

7.10. Tests JUnit der Klasse [ElectionsDaoJdbc]

  

Die Klasse [Test01] ist die nachfolgende Testklasse der Klasse [JUnit]:


package elections.dao.junit;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;


@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {

    // Schicht [DAO]
    @Autowired
    private IElectionsDao electionsDao;

    @Before
    public void init() {
        // Die Tabelle [LISTES] wird bereinigt
        // Wahllisten
        ListeElectorale[] listes = electionsDao.getListesElectorales();
        // Stimmen und Sitze werden auf 0 gesetzt und „false“ entfernt
        int voix = 0;
        int sièges = 0;
        boolean elimine = false;
        for (ListeElectorale liste : listes) {
            liste.setVoix(voix);
            liste.setSieges(sièges);
            liste.setElimine(elimine);
        }
        // Diese Daten werden mithilfe der Schicht [dao] dauerhaft gespeichert
        electionsDao.setListesElectorales(listes);
    }

    @Test
    public void testElections01() {
        ...
    }
}
  • Zeile 17: Die Annotation [RunWith], die eine Annotation [JUnit] (Zeile 6) ist, sorgt über die Klasse [SpringJUnit4ClassRunner] für die Integration mit Spring;
  • Zeile 16: Die Annotation [SpringApplicationConfiguration] ist eine Annotation [Spring] (Zeile 8), mit der die Konfigurationsklassen des Tests JUnit festgelegt werden können. Hier wird die Klasse [AppConfig] angegeben, die zur Konfiguration des Projekts verwendet wird. Damit stehen alle Spring-Objekte zur Verfügung, die durch diese Konfigurationsklasse definiert sind. Auf diese Weise kann in den Zeilen 21–22 eine Referenz auf die zu testende Schicht [DAO] injiziert werden;
  • Zeile 25: Die Annotation [Before] bezeichnet eine Methode, die vor jedem Test ausgeführt werden muss;
  • Zeilen 26–41: Die Methode [init] setzt die Stimmen und Sitze in den Listen der Tabelle [LISTES] auf Null und den Booleschen Wert [elimine] auf [false];

Die einzige Testmethode lautet wie folgt:


@Test
    public void testElections01() {
        System.out.println("testElections01-------------------------------------");
        // Abruf der Wahlkonfiguration
        ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
        int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
        double seuilElectoral = electionsConfig.getSeuilElectoral();
        Assert.assertEquals(6, nbSiegesAPourvoir);
        Assert.assertEquals(0.05, seuilElectoral, 1E-6);

        // Wahllisten
        ListeElectorale[] listes = electionsDao.getListesElectorales();
        // Anzeige der ausgelesenen Werte
        System.out.println("Nombre de sièges à pourvoir : " + nbSiegesAPourvoir);
        System.out.println("Seuil électoral : " + seuilElectoral);
        System.out.println("Listes en compétition ---------------------");
        for (int i = 0; i < listes.length; i++) {
            System.out.println(listes[i]);
        }

        // Stimmen und Sitze werden den Listen zugewiesen
        int voix = 0;
        int sièges = 0;
        boolean elimine = false;
        for (ListeElectorale liste : listes) {
            liste.setVoix(voix);
            liste.setSieges(sièges);
            liste.setElimine(elimine);
            voix += 10;
            sièges += 1;
            elimine = !elimine;
        }

        // Diese Daten werden mithilfe der Schicht [dao] dauerhaft gespeichert
        electionsDao.setListesElectorales(listes);

        // Die Daten werden erneut ausgelesen
        ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
        // Die gelesenen Daten werden überprüft
        Assert.assertEquals(7, listesElectorales2.length);
        voix = 0;
        sièges = 0;
        elimine = false;
        for (ListeElectorale liste : listesElectorales2) {
            Assert.assertEquals(voix, liste.getVoix());
            Assert.assertEquals(sièges, liste.getSieges());
            Assert.assertEquals(elimine, liste.isElimine());
            voix += 10;
            sièges += 1;
            elimine = !elimine;
        }
        System.out.println("Listes en compétition ---------------------");
        for (int i = 0; i < listes.length; i++) {
            System.out.println(listes[i]);
        }
    }
  • Zeilen 5–9: Es wird sichergestellt, dass der Inhalt der Tabelle [CONF] abgerufen werden kann;
  • Zeilen 11–19: Der Inhalt der Tabelle [LISTES] wird angezeigt. Hier finden keine Tests statt, sondern lediglich eine Sichtprüfung;
  • Zeilen 21–35: Man ändert in der Datenbank die Tabelle [LISTES], indem man den Listen Werte für ihre Felder [voix, sieges, elimine] zuweist;
  • Zeilen 37–51: Die Tabelle [LISTES] wird erneut ausgelesen und überprüft, ob das Ergebnis tatsächlich mit den eingegebenen Werten übereinstimmt;
  • Zeilen 52–55: Sichtprüfung;

Die mit einer implementierten Schicht [DAO] erzielten Konsolenergebnisse lauten wie folgt:

mars 11, 2015 4:50:00 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
INFOS: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
mars 11, 2015 4:50:00 PM org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
INFOS: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
mars 11, 2015 4:50:00 PM org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
INFOS: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
mars 11, 2015 4:50:00 PM org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
INFOS: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
mars 11, 2015 4:50:00 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
INFOS: Using TestExecutionListeners: [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@483bf400, org.springframework.test.context.support.DirtiesContextTestExecutionListener@21a06946]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.2.RELEASE)

[2015-03-11 16:50:01.272] - 11696 INFOS [main] --- org.eclipse.jdt.internal.junit.runner.RemoteTestRunner: Starting RemoteTestRunner on Gportpers3 with PID 11696 (started by ST in D:\data\istia-1415\eclipse\intro-jdbc\elections-jdbc-01)
[2015-03-11 16:50:01.317] - 11696 INFOS [main] --- org.springframework.context.annotation.AnnotationConfigApplicationContext: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@74ad1f1f: startup date [Wed Mar 11 16:50:01 CET 2015]; root of context hierarchy
mars 11, 2015 4:50:01 PM org.eclipse.jdt.internal.junit.runner.RemoteTestRunner logStarted
INFOS: Started RemoteTestRunner in 0.775 seconds (JVM running for 1.433)
testElections01-------------------------------------
Nombre de sièges à pourvoir : 6
Seuil électoral : 0.05
Listes en compétition ---------------------
{"id":1,"version":21,"nom":"A","voix":0,"sieges":0,"elimine":false}
{"id":2,"version":22,"nom":"B","voix":0,"sieges":0,"elimine":false}
{"id":3,"version":21,"nom":"C","voix":0,"sieges":0,"elimine":false}
{"id":4,"version":13,"nom":"D","voix":0,"sieges":0,"elimine":false}
{"id":5,"version":17,"nom":"E","voix":0,"sieges":0,"elimine":false}
{"id":6,"version":18,"nom":"F","voix":0,"sieges":0,"elimine":false}
{"id":7,"version":19,"nom":"G","voix":0,"sieges":0,"elimine":false}
Listes en compétition ---------------------
{"id":1,"version":21,"nom":"A","voix":0,"sieges":0,"elimine":false}
{"id":2,"version":22,"nom":"B","voix":10,"sieges":1,"elimine":true}
{"id":3,"version":21,"nom":"C","voix":20,"sieges":2,"elimine":false}
{"id":4,"version":13,"nom":"D","voix":30,"sieges":3,"elimine":true}
{"id":5,"version":17,"nom":"E","voix":40,"sieges":4,"elimine":false}
{"id":6,"version":18,"nom":"F","voix":50,"sieges":5,"elimine":true}
{"id":7,"version":19,"nom":"G","voix":60,"sieges":6,"elimine":false}
  • Zeilen 1–23: Spring-Test-Protokolle;
  • Zeilen 25–26: Inhalt der Tabelle [CONF];
  • Zeilen 27–34: der ursprüngliche Inhalt der Tabelle [LISTES];
  • Zeilen 35–42: Inhalt der Tabelle [LISTES] nach der Zuweisung von Werten zu den Feldern der Tabelle [voix, sieges, elimine];

Außerdem ist der Test JUnit erfolgreich:

 

7.11. Erstellung des Archivs [with-dependencies] aus der Ebene [dao]

Das endgültige Projekt weist folgende Architektur auf:

Zur Erinnerung: Die Maven-Konfiguration des Projekts lautet:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-dao-jdbc-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <dependencies>
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <!-- Tomcat JDBC -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>
        <!-- Bibliothek jSON -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot-Protokollierung -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
    </dependencies>

    <!-- Plugins -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>config.AppConfig</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <!-- zur Installation des Projekt-Artefakts im lokalen Maven-Repository -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  

Wir werden eine einzige JAR-Datei generieren, die die Klassen aller oben genannten JAR-Dateien sowie die des Projekts der Schicht [DAO] enthält. Dies geschieht durch eine Änderung in der Datei [pom.xml]:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-jdbc-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.2.RELEASE</version>
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        ...
    </dependencies>

    <!-- Plugins -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>console.Main</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • Zeilen 25–37: Konfigurieren ein Maven-Plugin zur Erstellung der JAR-Datei;
  • Zeile 15: gibt die für die Kompilierung zu verwendende Java-Version an;

Sobald diese Änderung vorgenommen wurde, kann die JAR-Datei wie folgt generiert werden: [1-10]:

  • in [5], den Projektordner über die Schaltfläche [6] auswählen;
  • in [7] einen Namen für die Ausführungskonfiguration vergeben;
  • in [8] die Liste der auszuführenden Maven-Ziele angeben:
    • [clean]: Leert den Ordner [target] des Projekts, in dem die generierte JAR-Datei abgelegt werden soll;
    • [compile]: kompiliert das Projekt;
    • [assembly:single]: Erzeugt eine einzige JAR-Datei, die alle Klassen des Projekts und seiner Abhängigkeiten enthält;
  • Bei [9-10] stellen Sie sicher, dass Sie über ein JDK (Java Development Kit) und nicht über ein JRE (Java Runtime Environment) verfügen. Der Unterschied besteht darin, dass JDK über einen Compiler verfügt, JRE hingegen nicht. Wenn Sie kein JDK haben, müssen Sie in [10] zusammen mit [11] eines hinzufügen. Befolgen Sie dazu die in Abschnitt 3.1 beschriebene Vorgehensweise;
  • in [17] führen Sie die Ausführungskonfiguration durch;
  • in [13] das generierte Archiv;

Dieses Archiv kann mit einem Entpacker geöffnet werden:

 

7.12. Test des Archivs der Schicht [DAO]

Erstellen wir ein Standard-Eclipse-Projekt (kein Maven) [1]:

Kopieren wir das Paket [dao.console] aus dem Projekt [elections-dao-jdbc-01] und fügen es in das Projekt [elections-dao-jdbc-02] [2] ein. Es treten zahlreiche Fehler auf, da die Klasse „[Main]“ auf Klassen verweist, die nicht in ihrem [Classpath] enthalten sind. Wir werden dieses Paket nun anpassen.

Zunächst erstellen wir im neuen Projekt einen Ordner „[lib]“ und darin die Datei „[2-8]“:

In [9] legen wir das im vorherigen Schritt erstellte Archiv im Ordner [lib] ab und ändern anschließend den Build-Pfad des Projekts:

  • in „[18]“; wir haben das zuvor erstellte Archiv der Ebene „[DAO]“ importiert;
  • In [19] weist das Projekt keine Fehler mehr auf;

Nun kann die Klasse [Main] ausgeführt werden. Es werden die gleichen Ergebnisse wie zuvor erzielt.