Deutsch English

Architektur Datenbank Client-Module XML-Module XML-Panel Features Beispiel Warenwirtschaft

XML-Module

XML-Module bieten den Vorteil, dass sie sehr einfach und äußerst schnell erstellt werden können. Dennoch bieten sie Entwicklern die Möglichkeit, nach Belieben durch eigenen Code Änderungen und Erweiterungen vorzunehmen. Ein XML-Modul stellt also ein voll funktionsfähiges Grundgerüst für die Datenbearbeitung zur Verfügung, welches - wenn erforderlich - angepasst und erweitert werden kann. Das Aussehen und die grundlegende Funktionalität von XML-Modulen wird durch XML-Dateien in dem Projekt definiert. Dabei werden u.a. die folgenden Punkte festgelegt:

Beispiel

An einem sehr einfachen Beispiel lässt sich am besten demonstrieren wie einfach und schnell es möglich ist, ein neues XML-Modul zu erzeugen. Im Folgenden gehen wir dabei davon aus, dass ein Projekt eingerichtet wurde und auch lauffähig ist. In unserem Beispiel hier möchten wir ein kleines Modul zur Verwaltung von Artikeln in einem Shop erstellen.

Datenbank

Wir beginnen mit der Definition der Datenbank-Tabelle, in der die einzelnen Datensätze für das Modul gespeichert werden sollen. Diese soll wie folgt aussehen:

<table name="Artikel" description="Artikel">
  <column name="nArtikelNr"     type="int"                    nullable="false"/>
  <column name="sSachnummer"    type="varchar"  length="12"   nullable="false"/>
  <column name="sBezeichnung"   type="varchar"  length="30"   nullable="false"/>
  <column name="sBeschreibung"  type="varchar"  length="1024" nullable="true"/>
  <column name="sArtikelGruppe" type="char"     length="4"    nullable="true"/>
  <column name="sDimension"     type="char"     length="2"    nullable="true"/>
  <column name="fPreis"         type="float"                  nullable="true"/>
  <column name="sLagerort"      type="char"     length="1"    nullable="true"/>
  <column name="fBestand"       type="float"                  nullable="true"/>
  <column name="sLieferant"     type="varchar"  length="30"   nullable="true"/>
  <column name="dtAufgenommen"  type="datetime"               nullable="true"/>
  <primaryKey name="pkArtikel">
    <column name="nArtikelNr"/>
  </primaryKey>
</table>

Die Angaben in diesem XML-Segment dürften weitgehend selbsterklärend sein. Wir definieren eine Tabelle mit dem Namen Artikel. Diese hat mehrere Spalten/Felder (Tag column) und einen Primärschlüssel (Tag primaryKey) in Form einer laufenden Nummer für jeden Datensatz. Bei den einzelnen Spalten werden u.a. Name, Typ, evtl. eine Länge sowie der nullable-Zustand festgelegt. Nach einem Server-Neustart wird die Tabelle so wie angegeben in der Datenbank erzeugt.

Panel

In einer weiteren XML-Datei wird das Aussehen und die Funktionalität des Panels (also der Bearbeitungsmaske) beschrieben. Dazu werden auf dem Panel (Swing-)Komponenten (Widgets) platziert. Dieses Panel soll die Möglichkeit bieten, nach Datensätzen zu suchen, diese zu verändern, zu löschen und neue Datensätze anzulegen. Es wird also die klassische CRUD-Funktionalität abgebildet. Zunächst werden einige allgemeine Daten wie der Name der Datenbank-Tabelle, Primärschlüssel und die ID des zu verwendenden Such-Dialoges festgelegt.

<?xml version="1.0" encoding="UTF-8"?>
<xmlpanel>
  <configuration
    table="Artikel"
    searchDialog="artikelSuchDialog"
    recordControlPattern="Artikel: {0}"
  >
    <primaryKey>
      <column name="nArtikelNr" type="INT" source="AUTO" />
    </primaryKey>
  </configuration>

Es folgt die Definition des Such-Dialoges, der für die Suche nach Datensätzen verwendet werden soll. Dieser Dialog wird geöffnet, sobald in dem Modul bzw. Panel auf den Suchen-Button geklickt wird.

<searchDialog
  id="artikelSuchDialog"
  title="Artikel-Suche"
  autoResizeMode="AUTO_RESIZE_OFF"
  size="670 500"
  table="Artikel"
  where=""
  orderBy="sBezeichnung"
>
  <column title="Bezeichnung" field="sBezeichnung" width="220" minWidth="10" maxWidth="300"/>
  <column title="Sachnummer"  field="sSachnummer"  width="100" minWidth="10" maxWidth="120"/>
  <column title="Artikelgruppe" field="sArtikelGruppe" width="100" minWidth="10"
      maxWidth="120" type="COMBOBOX">
    <item text="Buch"       value="BUCH" />
    <item text="CD"         value="CD  " />
    <item text="Kleidung"   value="KLEI" />
    <item text="Elektronik" value="ELEK" />
  </column>
  <column title="Preis" field="fPreis" width="60" minWidth="10"
      maxWidth="120" type="NUMBER" alignment="RIGHT" pattern="#.00"/>
  <column title="Dimension" field="sDimension" width="80" minWidth="10"  
      maxWidth="120" type="COMBOBOX">
    <item text="Stück"      value="ST" />
    <item text="Meter"      value="M " />
    <item text="Liter"      value="L " />
    <item text="Kilogramm"  value="KG" />
  </column>
  <column title="Bestand" field="fBestand" width="70" minWidth="10"
      maxWidth="120" type="NUMBER" alignment="RIGHT" pattern="#.0"/>
</searchDialog>

Dabei werden Angaben zu dem Aussehen des Dialoges, den einzelnen Spalten sowie zu der Datenbank-Tabelle und den Feldern gemacht. Der Such-Dialog sieht dann später im Programm in etwa wie folgt aus:

Beispiel für einen Such-Dialog: Artikel-Suche

Oberhalb der Tabelle mit den gefundenen Datensätzen befinden sich Textfelder und Comboboxen mit denen die Suchergebnisse eingeschränkt werden können. Dabei ist es auch möglich, Wildcards wie * für beliebig viele Zeichen oder ? für ein Zeichen zu verwenden. Bei Zahlenwerten wie bei dem Preis oder dem Bestand kann ein von-bis-Bereich angegeben werden. Mit dem Doppelklick auf einen Datensatz (oder Auswahl + Ok) wird dieser in die Bearbeitungsmaske übernommen und kann dort verändert werden.

Das Aussehen der Bearbeitungsmaske mit seinen Komponenten definieren wir nun im Folgenden. Es beginnt mit der Definition eines Containers (Panel), in dem die einzelnen Komponenten (Widgets) platziert werden. Wird beim Toplevel-Panel kein Layout-Manager angegeben, so wird standardmäßig das Gridbaglayout verwendet. Dieses ordnet die Widgets in Form einer Tabelle an. Innerhalb des Panels liegen zunächst ein Label und ein Textfeld zur Eingabe der Sachnummer:

<panel id="main" widgetGroupID="">
  <label text="Sachnummer:"
    constraints="0 0 1 1 0.0 0.0 EAST NONE 5 5 0 0 0 0"
  />
  <textField
    id="idSachnummer"
    name="Sachnummer"
    toolTipText="Sachnummer des Artikels"
    constraints="1 0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
    obligatory="true"
    size="100 21"
    field="sSachnummer"
    type="STRING"
  />

Das Attribut constraints legt die Platzierung des Widgets innerhalb des Layout-Managers fest, hier also innerhalb des Gridbaglayouts. Die einzelnen Werte definieren die Parameter für ein GridBagConstraints-Objekt und legen in dieser Reihenfolge Spalte, Zeile, Breite in Zellen, Höhe in Zellen, horizontale und vertikale Ausdehnung (bei automatischer Größenanpassung), Ausrichtung, automatische Größenanpassung und Ränder fest (genaueres dazu finden Sie in der Java-Dokumentation). Spalte und Zeile können auch relativ zu der vorherigen Komponente festgelegt werden wie weiter unten noch zu sehen sein wird.

Das Textfeld bekommt eine ID, um später im Quellcode auf diese Komponente zugreifen zu können, falls dieses erforderlich sein sollte (in unserem Beispiel ist es nicht erforderlich und wir hätten die Angabe der ID also auch weglassen können). Weiter werden ein Name (für Fehlermeldungen), ein Tooltiptext und die Größe des Textfeldes festgelegt. Schließlich auch das Datenbankfeld, in dem die vom Anwender eingegebenen Werte gespeichert werden sollen. Der angegebene Datentyp sorgt für eine Überprüfung und Konvertierung des eingegebenen Wertes. Mit obligatory="true" wird festgelegt, dass eine Eingabe durch den Anwender zwingend erforderlich ist. Ansonsten wird die Speicherung des Datensatzes nicht durchgeführt und der Anwender bekommt eine Fehlermeldung.

Als nächstes folgt analog ein Eingabefeld für die Bezeichnung des Artikels:

<label text="Bezeichnung:"
  constraints="2 0 1 1 0.0 0.0 EAST NONE 5 15 0 0 0 0"
/>
<textField
  id="idBezeichnung"
  name="Bezeichnung"
  toolTipText="Bezeichnung des Artikels"
  constraints="3 0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  obligatory="true"
  size="230 21"
  field="sBezeichnung"
  type="STRING"
/>

Die Artikelgruppe soll über eine Combobox auswählbar sein:

<label text="Artikelgruppe:"
  constraints="0 +1 1 1 0.0 0.0 EAST NONE 5 5 0 0 0 0"
/>
<comboBox
  id="idArtikelgruppe"
  name="Artikelgruppe"
  toolTipText="Artikelgruppe"
  constraints="1 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  field="sArtikelGruppe"
  type="STRING"
>
  <item text="Buch"       value="BUCH" />
  <item text="CD"         value="CD  " />
  <item text="Kleidung"   value="KLEI" />
  <item text="Elektronik" value="ELEK" />
</comboBox>

Der Inhalt der Combobox wird hier der Einfachheit halber direkt in der XML-Datei vorgegeben. Dabei wird jeweils der Anzeigewert und der dafür zu speichernde Wert angegeben. Es ist aber auch möglich, als Datenquelle für die Items einer Combobox eine Datenbank-Tabelle zu verwenden. In einer realen Implementierung wäre das sicher auch der sinnvollere Weg. Die Bearbeitung dieser Tabelle würde dann in einem Stammdaten-Modul erfolgen, welches ebenfalls wieder sehr einfach durch ein XML-Modul zu realisieren wäre.

Es geht weiter mit einem Feld, in dem wir das Datum speichern möchten zu dem der Artikel in das Sortiment aufgenommen wurde.

<label text="Aufgenommen:"
  constraints="2 +0 1 1 0.0 0.0 EAST NONE 5 5 0 0 0 0"
/>
<dateField
  id="dateAufgenommen"
  name="Aufgenommen"
  format="DATE"
  toolTipText="in den Bestand aufgenommen am"
  constraints="3 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  size="90 21"
  field="dtAufgenommen"
  type="DATE"
/>

Sobald ein Datums-Eingabefeld den Focus erhält, wird ein Kalender aufgeklappt, in dem der Anwender einen Tag auswählen kann. Das Datum kann natürlich auch von Hand eingegeben werden.

Datums-Eingabefeld mit einem verknüpften Popup Kalender

Der Preis des Artikels wird in einem Nummern-Eingabefeld eingegeben:

<label text="Preis:"
  constraints="0 +1 1 1 0.0 0.0 EAST NONE 5 5 0 0 0 0"
/>
<numberField
  id="idPreis"
  name="Preis"
  toolTipText="Preis"
  constraints="1 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  obligatory="true"
  pattern="#.00"
  horizontalAlignment="RIGHT"
  size="70 21"
  field="fPreis"
  type="FLOAT"
/>

Hier wurde ein Zahlenformat für die Anzeige hinterlegt, damit der Preis immer mit zwei Nachkommastellen angezeigt wird. Außerdem soll die Zahl rechtsbündig in dem Feld ausgerichtet werden.

Die Eingabefelder für Dimension, Lagerort, Bestand und Lieferant werden mit Comboboxen und Textfeldern realisiert:

<label text="Dimension:"
  constraints="2 +0 1 1 0.0 0.0 EAST NONE 5 15 0 0 0 0"
/>
<comboBox
  id="idDimension"
  name="Dimension"
  toolTipText="Dimension"
  constraints="3 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  field="sDimension"
  type="STRING"
>
  <item text="Stück"      value="ST" />
  <item text="Meter"      value="M " />
  <item text="Liter"      value="L " />
  <item text="Kilogramm"  value="KG" />
</comboBox>

<label text="Lagerort:"
  constraints="0 +1 1 1 0.0 0.0 EAST NONE 5 15 0 0 0 0"
/>
<comboBox
  id="idLagerort"
  name="Lagerort"
  toolTipText="Lagerort"
  constraints="1 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  field="sLagerort"
  type="STRING"
>
  <item text="Hauptlager"   value="H" />
  <item text="Außenlager 1" value="1" />
  <item text="Außenlager 2" value="2" />
</comboBox>

<label text="Bestand:"
  constraints="2 +0 1 1 0.0 0.0 EAST NONE 5 5 0 0 0 0"
/>
<numberField
  id="idBestand"
  name="Bestand"
  toolTipText="Bestand"
  constraints="3 +0 1 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  pattern="#.##"
  horizontalAlignment="RIGHT"
  size="70 21"
  field="fBestand"
  type="FLOAT"
/>

<label text="Lieferant:"
  constraints="0 +1 1 1 0.0 0.0 EAST NONE 5 15 0 0 0 0"
/>
<textField
  id="idLieferant"
  name="Lieferant"
  toolTipText="Lieferant"
  constraints="1 +0 3 1 0.0 0.0 WEST NONE 5 5 0 0 0 0"
  size="300 21"
  field="sLieferant"
  type="STRING"
/>

Ein mehrzeiliges Textfeld dient zur Eingabe einer Beschreibung für den Artikel:

<label text="Beschreibung:"
    constraints="0 +1 1 1 0.0 0.0 NORTHEAST NONE 5 15 0 0 0 0"
/>
<textArea
    id="idBeschreibung"
    name="Beschreibung"
    toolTipText="Beschreibung"
    constraints="1 +0 3 1 10.0 0.0 WEST HORIZONTAL 5 5 0 0 0 0"
    size="300 80"
    field="sBeschreibung"
    type="STRING"
/>

Abgeschlossen wird das Panel mit einem unsichtbaren Label, welches den ganzen noch vorhandenen Platz im Panel einnehmen soll. Damit wird erreicht, dass alle anderen Komponenten in der linken oberen Ecke des Panels angeordnet werden. Ansonsten wären alle mittig angeordnet, was nicht sehr schön aussehen würde.

    <label text=""
      constraints="0 +1 4 1 1.0 1.0 WEST BOTH 0 0 0 0 0 0"
    />
  </panel>
</xmlpanel>

Damit ist die Definition des Panels abgeschlossen.

Modul

Nachdem das Panel nun definiert ist, muss dieses einem Modul hinzugefügt werden. Dazu wird eine weitere XML-Datei angelegt, die das Modul beschreibt. Diese ist sehr kurz und sieht wie folgt aus:

<?xml version="1.0" encoding="UTF-8"?>
<module>
  <panel
    id="idArtikel"
    name="Artikel"
    toolTipText="Modul zur Artikelverwaltung"
    class="de.aviantes.jaf.client.xmlpanel.editpanel.XMLEditPanel"
    parameter="modules/testmodules/artikel/ArtikelPanel.xml"
  />
</module>

Das Modul hat hier nur ein Panel. Zu diesem wird die Java-Klasse angegeben, die für die Implementierung des kompletten Panels zuständig ist. Hier wird direkt die Klasse aus dem Framework angegeben, da wir keine Erweiterungen in einer abgeleiteten Klasse benötigen. Als Parameter wird der (Klassen-)Pfad der XML-Datei des Panels eingetragen, also genau die Datei, die wir weiter oben erstellt haben.

Zum Schluss muss dieses neu definierte Modul in die Liste aller Client-Module eingetragen werden, damit der Client von der Existenz dieses Moduls erfährt. Diese Liste wird wiederum in Form einer XML-Datei verwaltet. Der entsprechende Eintrag sieht wie folgt aus:

<module nr="100" id="ARTIKEL_MODUL" name="Artikel"
  class="de.aviantes.jaf.client.module.general.GeneralModul"
  parameter="modules/testmodules/artikel/ArtikelModul.xml" />

Hier wird nun die Klasse für das Modul hinterlegt. Dieses ist eine Standardklasse aus dem Framework, da kein selbst implementiertes Modul erstellt wurde. Als Parameter wird der Pfad zu der Modul-XML-Datei eingetragen.

Damit ist die Erstellung eines einfachen neuen Moduls abgeschlossen. In der Modul-Verwaltung des Clients kann dieses Modul nun in ein Menü gehängt und mit Rechten versehen werden.

Modul-Verwaltung im Client

Das Modul ist voll funktionsfähig und sieht im Endergebnis so aus:

Beispiel eines erzeugten Moduls: Artikel-Modul

Es wurde ein Datensatz geladen und in den Bearbeitungs-Modus gegangen. Die Buttons im linken oberen Bereich des Panels haben dabei folgende Funktionen:

Über die Buttons auf der rechten Seite kann durch die Datensätze aus der letzten Suche geklickt werden bzw. es kann direkt einer in der Combobox ausgewählt werden.

Eine Erweiterung dieses Panels um weitere Widgets ist jetzt sehr einfach möglich. Es müssen nur weitere Datenbankfelder in der Tabelle angelegt und weitere Widgets in der XML-Datei für das Panel hinzugefügt werden. Sobald einige Panels fertig sind, ist das ein reines Copy & Paste mit kleineren Anpassungen, die innerhalb von Minuten erledigt sind.