BigBasti's Blog About Me & my Digital Lifestyle

29Jan/104

Tutorial: Model-View-Controller (MVC) Struktur in Java Projekten nutzen

Die MVC-Architektur erfreut sich in letzter Zeit sehr hoher Beliebtheit, ständig hört man über neue Frameworks die auf diese Architektur schwören. (Beispiele: Spring Framework, ASP.NET MVC, Objective C...)

Bild 1: Der Aufbau - Quelle: Wikipedia

Doch was steckt eigentlich dahinter und warum sollte man seine Struktur überdenken?

Ich denke jeder, der eine Anwendung geschrieben hat schon die Erfahrungen gemacht, dass der Code immer unübersichtlicher wird, je größer ein Projekt wird.

Hier setzt MVC an, denn durch die strickte Teilung von Präsentation (View), der Programmlogik (Controller) und der Datenschicht (Model) erhöht sich die Lesbarkeit und Wartbarkeit des Codes. So kann man ein Projekt viel einfacher um weitere Funktionen ergänzen ohne den halben Sourcecode durchzugucken.

In diesem Artikel möchte ich diese Architektur an einem kleinem Java Beispiel demostrieren. Das Programm ist natürlich nicht wirklich zu gebrauchen, es soll lediglich das Vorgehen nahelegen.

Wie bereits oben erwähnt soll die Präsentation, also das was der Benutzer zu sehen bekommt (üblicherweise das Formular, also die GUI) von der Logik getrennt werden. Deshalb dürfen in der View-Klasse nur Sachen stehen, die der Darstellung der Elemente dienen. Das was passiert wenn man eins dieser Elemente anklickt wird dann über den Controller gesteuert.

Die Berechnungen an sich stehen dann im Model, wo auch alle Objekte verweilen, die unser Programm nutzt.

Unser Demo Programm ist sehr einfach aufgebaut, wir haben nur vier Klassen:

Bild 2: Der Aufbau des Projekts

Bevor ihr euch wundert, das Programm soll uns später die Quadratwurzel einer Zahl berechnen.

An Bild 1 könnt ihr erkennen, dass die Controller Klasse die wichtigste ist, da sie eigentlich das Ganze Programm steuert und dafür sorgt, dass die View und das Model sich verständigen können. Deswegen muss die Controller Klasse die anderen Klassen kennen, die anderen brauchen sich gegenseitig aber nicht!


public class Main {

    static WurzelController controller;

    /**
     * Diese Klasse wird nur dazu benutzt alle nötigen
     * Komponenten zu Initialisieren und die erste
     * View anzuzeigen
     */
    public static void main(String [] args){
        controller = new WurzelController();

        controller.showView();
    }
}

Die Klasse Main macht hier auch nichts anderes, als die anderen Klassen zu instanzieren und uns die View anzuzeigen.

An den Konstruktoren kann man auch die Abhängigkeiten erkennen. Eigentlich macht man der View auch das Model bekannt (siehe Bild 1), aber da unser Beispiel zu simpel ist können wir uns diese Bekanntschaft sparen.

Das Model ist ebenfalls sehr einfach aufgebaut, es enthält die nötigen Rechenoperationen die für unser Programm wichtig sind und bietet öffentliche Methoden mit denen der  Controller die Werte abfragen kann


/**
 * Das Model ist komplett unabhängig von den anderen
 * Klassen und weiß nicht was um ihn herum geschieht.
 * Es ist völlig egal ob man dieses Model aus einem
 * Fenster oder einer Konsolen Eingabe verwendet -
 * beiden würde funktionieren.
 */</pre>
<pre>public class WurzelModel {

    long _value;

    public WurzelModel(){
        zurückSetzen();
    }

    public void zurückSetzen(){
        this._value = 0;
    }

    public void berechneWurzel(long wert){
        this._value =  (wert * wert);
    }

    public long getWurzel(){
        return this._value;
    }
}

Wichtig hierbei ist, dass diese Klasse keine Beziehungen zu den anderen Klassen hat! Man muss sie so aufbauen, dass man sie ohne eine Änderung in einem anderen Projekt benutzen könnte.

Unsere View ist ganz typisch aufgebaut. Es werden die Steuerelemente initialisiert und auf der JForm plaziert, desweiteren muss die View Methoden bieten, mit denen man die ActionListener für die auf dem Formular liegenden Steuerelemente setzen kann. Über diese wird unser Controller mit der View kommunizieren. Dazu kommen noch die getter und setter Methoden für die Textfelder, diese werden benötigt, damit man aus dem Controller Zugriff darauf hat!


/** Die View-Klasse diese Enthält nur die Präsentation
 * hier sollte man keinerlei Programmlogik finden
 * alle Berechnungen und Reaktionen auf Benutzeraktionen
 * sollten allesammt im Controller stehen
 */</pre>
<pre>public class WurzelView extends JFrame{

    private JLabel lbl1 = new JLabel("Eingabe: ");
    private JTextField txtEingabe = new JTextField(3);
    private JButton cmdCalc = new JButton("Wurzen Berechnen >");
    private JTextField txtErg = new JTextField(5);
    private JButton cmdClear = new JButton("Zurüclsetzen");

    public WurzelView(){
        super("Wurzel Berechnen");

        initForm();
    }

    /**
     * Die JForm initialisieren und alle Steuerelemente
     * darauf positionieren
     */
    private void initForm(){
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new FlowLayout());
        this.setBounds(200, 200, 500, 100);

        this.add(lbl1);
        this.add(txtEingabe);
        this.add(cmdCalc);
        this.add(txtErg);
        this.add(cmdClear);

    }

    public void resetView(){
        this.txtEingabe.setText("");
        this.txtErg.setText("");
    }

    public String getEingabe(){
        return this.txtEingabe.getText();
    }

    public void setErgebnis(String erg){
        this.txtErg.setText(erg);
    }

    /**
     * Funktionen bereitstellen, mit denen man später aus
     * dem Controller die nötigen Listener hinzufügen kann
     */
    public void setWurzelBerechnenListener(ActionListener l){
        this.cmdCalc.addActionListener(l);
    }

    public void setResetFormListener(ActionListener l){
        this.cmdClear.addActionListener(l);
    }
}

Bleibt nur noch der Controller. Dieser ist auch sehr simpel aufgebaut:


/**
 * Der Controller muss beide die View und das Model kennen
 * da dieser für die Kommunikation zwischen den Beiden sorgt
 */
public class WurzelController {

    private WurzelView _view;
    private WurzelModel _model;

    public WurzelController(){
        this._model = new WurzelModel();
        this._view = new WurzelView;

        addListener();
    }</pre>
<pre>
    public void showView(){
        this._view.setVisible(true);
    }</pre>
<pre>
/**
     * Die Listener, die wir aus den Internen Klassen generieren
     * werden der View bekannt gemacht, sodass diese mit
     * uns (dem Controller) kommunizieren kann
     */
    private void addListener(){
        this._view.setWurzelBerechnenListener(new WurzelBerechnenListener());
        this._view.setResetFormListener(new ResetFormListener());
    }

    /**
     * Inneren Listener Klassen implementieren das Interface ActionListener
     *
     * 1: Hier wird erst der eingegebene Wert aus der View geholt
     * 2: Der Wert wird dem Model übergeben und die Wurzel berechnet
     * 3: Die berechnete Wurzel wird aus dem Model geladen und
     * 4: Wieder der View zum Darstellen übergeben
     *
     * ACHTUNG: Fehlerprüfung muss noch implementeirt werden
     */
    class WurzelBerechnenListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            long wert = Long.valueOf(_view.getEingabe());
            _model.berechneWurzel(wert);
            _view.setErgebnis(String.valueOf(_model.getWurzel()));
        }
    }

    /**
     * Hier wird dem View und dem Model gesagt ihre gespeicherten
     * Werte zu löschen.
     */
    class ResetFormListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            _view.resetView();
            _model.zurückSetzen();
        }
    }
}

Wie man sieht werden hier nur die dem Konstruktor übergebenen Referenzen auf die View und das Model gespeichert und dann die Erzeugten ActionListener der View übergeben.

Die ActionListener sind interne Klassen die nun automatisch von unserer View aufgerufen werden, wenn der Benutzer einen der Buttons klickt! Praktisch oder?

Wenn man die WurzelBerechnenListener Klasse anschaut kann man auch die Typische Kommunikation zwischen den Schichten sehen. Die View lößt ein Event aus, für dessen Abhandlung der Controller verantwortlich ist. Nun holt der Controller sich alle nötigen Daten aus der View (in diesem Fall ist es nur ein Wert, die Eingabe) und lässt das Model das Quadrat berechnen. Schließlich holt der Controller sich die berechneten Daten zurück und lässt die View diese anzeigen.

Im Grunde gar nicht kompliziert oder? So sieht das Fenster zur Laufzeit aus:

Bild 3: Das Programm zur Laufzeit

Wenn man ein neues Projekt anfängt sollte man sich natürlich erst einmal genau überlegen ob man die MVC Architektur benutzt, denn diese ist bei kleinen Projekten mit einem höheren Aufwand verbunden. Wenn man aber weiß, dass in absehbarer Zeit an dem Programm weitere Änderungen oder Erweiterungen vorgenommen werden sollte man MVC in Betracht ziehen!

ACHTUNG: Habe eben erst gemerkt, dass ich überall "Wurzel" geschrieben habe, das ergibt natürlich gar keinen Sinn! :D oh mann, muss wohl sehr verwirrt gewesen sein! Quadrat müsste es natürlich heißen. :) Keine Lust das nun alles nochmal zu ändern!

Die Sourcen könnt ihr hier laden: Download

VN:F [1.9.4_1102]
Rating: 7.6/10 (8 votes cast)
27Jan/105

iPad – Apple’s neues Produkt – Mein Eindruck

Nun kann die Welt endlich wieder ruhig schlafen, Apple hat ihr neues Gerät vorgestellt - das iPad. Dieses soll die Lücke zwischen iPhone/iPod und dem Macbook schließen.

Quelle: gdgt.com <- Mehr Bilder

Ich persönlich war auch schon gespannt auf das lang erwartete "Tablet", die Medien überschlugen sich, jeden Tag neue Gerüchte immer wildere Photoshopkreationen.

Nun ist es da, und ich muss ehrlich sagen, ich bin enttäuscht. Optisch ist das Gerät, das so ähnlich aussieht wie der Bildschirm von einem Allu-Macbook, sehr chic - wie erwartet, aber alles in einem kann man es als einen großen iPod bezeichnen.

Nun darf man aber nicht, wie bei Apple Geräten üblich, daher kommen und das iPad mit anderen Tablet PCs vergleichen. Das wäre dann der berühmte Äpfel mit Birnen vergleich.

Ich will erst einmal ausführen, was mir gefallen hat. Das iPad ist ein rundum schönes Gerät, Apple sagt selber es soll primär für "Browsing, Email, Photos, Videos, Musik, Games & eBooks" gut sein. Und nach der Keynote weis ich eins, dafür ist dieses Gerät wie gemacht.

Es kommt mir der vom iPhone und iPod bekannten Benutzeroberfläche daher, die an den größeren Bildschirm angepasst wurde und um einige neue Funktionen ergänzt wurde wie zB. PopUp Menüs.

Hut ab, das hat Apple echt drauf, die Bedienung ist super gelungen, ich als iPhone & Macbook Besitzer weiß das echt zu schätzen! Die Mail und Kalender App wurde von Grund auf überarbeitet und sieht richtig chic aus!

Ein weiteres Feature, dass wohl am wichtigsten ist, ist die Kompatibilität zu den iPhone Apps, denn man kann alle jeztschon verfügbaren Apps installieren und in voller Auflösung genießen. Desweiteren wird das iPad wohl ein starker Konkurent zu dem Kindle von Amazon werden, da das eBook Programm einfach nur grandios aussieht!

Dazu kommt noch, dass Apple eine Akkulaufzeit von 10 Stunden angibt, das ist beeindruckend!

Doch leider gibts auch Schattenseiten. Denn das iPad kommt wie auch das iPhone mit dem Safari Mobile daher, das bedeutet, dass man weiterhin keine Flash Unterstützung hat und nur YouTube Videos anschauen kann (über ein Extra Programm). Warum nur? Dieses Gerät schreit einfach danach! Wenn ich allein an die vielen Flash-basierenden Browsergames denke! Gut man kann natürlich argumentieren dass es im Appstore 140.000 Programme & Spiele gibt aber Flash ist heute (leider) immer noch eine der wichtigsten Web Technologien.

Desweiteren kann man keine Dateien herunterladen und auch keine hochladen, was man zwar nicht oft benötigt aber es dann bitter ist wenn man es mal braucht!

Weiter fällt auf, dass das iPad "nur" einen Dockconnector Port an der Unterseite hat und einen Kopfhörerstecker - exakt die selben Anschlüsse wie auf dem iPhone, inklusive der lauter/leiser Tasten und dem Stummknopf.

Warum findet man hier keinen USB-Stecker? Dick genug ist die Seite des iPads (glaube ich) oder wenigstens ein SD-Karten Slot. Diesen kann man zwar mit einem Adapter nachrüsten aber das ist doch Käse!

Ein weiteres Manko ist, dass es kein "richtiges" Betriebssystem hat. Das iPad OS (?) ist wie auch das iPhone nicht in der Lage mehrere Applikationen zur gleichen Zeit auszuführen (Multitasking). Wobei man diesen Punkt auch als Vorteil sehen kann, da das Gerät dadurch schneller und einfacher zu bedienen ist.

Genau aus diesem Grund kann man das Gerät auch nicht mit anderen Tablets vergleichen, da diese mit Windows - einem "vollen" Betriebssystem - laufen.

Leider ist genau hier auch der Nachteil der Windows Tablets, denn ein angepasstes System ist natürlich viel effizienter und einfacher. Doch hat Apple hier wiedereinmal seine Sperrvorrichtungen aufgefahren und unterbietet jegliche Zugriffe auf das Dateisystem, was bedeutet, dass man auch das iPad erst "jailbraken" muss bevor man es "vernünftig" benutzen kann!

Das Günstigste Model kostet 499 Dollar (also ca 499 Euro im Apple Wechselkurs) und kann nur 16 GB Speicher bieten und hat dazu noch kein 3G Modul integriert! Hier könnte man natürlich wieder sagen man bekommt ein gutes Netbook für das Geld - aber das wären wieder Äpfel und Birnen.

Apple hat den Zweck des iPads definiert und man muss als Kunde damit leben. Man bekommt nicht mehr und nicht weniger!

Mein Eindruck ist, dass Apple hier einen großen iPod Touch mit 16 GB Speicher für $500 verkauft. Im Vergleich zu der iPhone Präsentation verspüre ich keinerlei Begeisterung für das Produkt eher im Gegenteil. Aber vielleicht muss man nur eine Weile warten, bis die 2te Version des iPad herauskommt und alles besser macht, wie einst beim iPhone!

VN:R_N [1.9.4_1102]
Rating: 0.0/10 (0 votes cast)
26Jan/100

Interessantes aus der Java Welt Teil 1

In diesem Beitrag möchte ich mal auf ein paar Besonderheiten eingehen, denen man wohl nicht jeden Tag beim Programmieren begegnet und die teilweise auch anders arbeiten, als wie man im Kopf denkt!

Ich möchte hier mal mit einem kleinem Beispiel anfangen. Guckt euch mal den kurzen Quellcode hier an und versucht zu berechnen was nach der Ausführung des Codes auf dem Bildschirm ausgegeben wird, bzw. lässt sich dieser Code überhaupt kompilieren?


public class Main {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        int c = a+++b;

        System.out.println(c);
    }
}

Kompilieren lässt es sich aber was wird nun ausgegeben? Was passiert wenn man ein dreifaches Plus benutzt?

Naja das Ergebnis ist in jedem Fall 5. Doch um zu erfahren warum das so ist, muss man ersteinmal wissen, was der Kompiler da eigentlich macht, denn wir haben 2 Möglichkeiten die ausgeführt werden können:

int c = a_+_++b;   //1: Präfix Inkrement ( _ soll ein Leerzeichen darstellen)
int c = a++_+_b;   //2: Postfix Inkrement

Beide Rechenwege existieren doch nur einer wird ausgeführt, nämlich der 2te, denn nur der liefert als Ergebnis 5 zurück! Führt man den ersten Rechenweg aus erhält man 6 als Ergebnis!

Wenn der Kompiler diese Zeile liest und das Plus nach dem a gelesen hat erwartet er nun eigentlich den zweiten Summanden findet aber ein weiteres Plus, so wird erstmal die a++ Anweisung ausgeführt und anschließend + b dazu gerechnet.

Warum ist das so? Eigentlich wird doch in beiden Rechenwegen eine der Variablen um 1 (durch das ++ inkrement) erhöht, oder etwa nicht? Betrachten wir dazu dieses kleine Beispiel:


    public static void test(){
        int a = 2;
        a = ++a;

        int b = 2;
        b = b++;

        System.out.println(a);  //Ergibt 3
        System.out.println(b);  //Ergibt 2
    }

Im Falle von a wird der Wert von a erst erhöht und dann zugewiesen, dafür sorgt das so genannte Präfix (++a) da die Pluse auf der linken Seite stehen. Bei b wird erst der Wert von b zugewiesen und dann erhöht. Dadurch dass der Wert aber schon zugewiesen wurde verfällt diese Erhöhung  wieder. Wenn die Operatoren auf der rechten Seite stehen nennt man sie postfix.

Wie man sieht stecht doch einiges hinter einer solchen kleinen Anweisung. Deswegen sollte man hier (wenn man will, dass der erste Fall eintritt) immer Leerzeichen setzen und dem Kompiler so genau sagen, welche operation durchgeführt werden soll!

Benutzt beim Übergeben von Werten diese Schreibweise möglichst nicht, da es zu unerwarteten Werten kommen kann. Um hier sicher zu gehen und nichts zu reskieren kann man auch die etwas längere Schreibform benutzen:

starteFunktion(variable++); //stattdessen kann man auch das nutzen:
starteFunktion(variable + 1);

Die zweite Variante übergibt ebenfalls den Wert von variable um 1 erhöht, ändert aber nicht deren Wert!

Wer mehr zu diesem Thema wissen möchte kann hier gucken: Galileo Java ist auch eine Insel

VN:F [1.9.4_1102]
Rating: 10.0/10 (2 votes cast)
12Jan/100

Java: DB4O Anbindung in einer Client / Server umgebung

Da ich mich zur Zeit selbst mit diesem Thema auseinander setze, dachte ich, es könnte nicht schaden ein kleines Tutorial zu schreiben. Hier möchte ich die ersten Schritte erklären und an ein paar Beispielen demonstrieren.

DB4O steht für Database for Objects und ist, wie der Name es schon sagt eine Datenbank für Objekte. Im Unterschied zu einer SQL Datenbank (wie MySQL) werden hier keine Tabellen erstellt, in denen die Daten gesichert werden, sondern die Objekte "als Ganzes" in die Datenbank geschrieben.

Das bringt einen gewaltigen Vorteil, denn wenn man ein Objekt in einer herkömmlchen relationalen Datenbank sichern möchte muss man es in einzelne Teile zerlegen und diese Teile dann in die Tabellen schreiben. Beim Auslesen wieder das ganze Spielchen umgekehrt, man sucht sich alle Werte des Objekts wieder aus der Datenbank zusammen um dann eine neue Instanz davon zu erstellen und mit den alten Werten zu füllen.

All das ist mit DB4O nicht mehr nötig, da hier die Datenbank diese Aufgaben für uns übernimmt, somit ist es sehr einfach für uns auch sehr komplexe Objekte schnell und bequem zu sichern und zu laden.

Fangen wir aber ersteinmal mit der Installation an

Die Installation, wenn man diese so nennen kann ist bei DB4O sehr einfach, und besteht nur darin, die nötigen Librarys in das Projektverzeichnis zu kopieren. Somit ist keine Installation beim Client mehr nötig, das Programm bringt dann alles was nötig ist selbst mit!

Als erstes benötigt ihr die Librarys von der DB4O - Homepage

Nach dem ihr diese heruntergeladen habt erstellt ihr ein "lib" Verzeichnis in eurem Projektordner.

In diesen neu erstellten Ordner entpackt ihr die *.jar Dateien aus dem heruntergeladenen Archiv aus dem gleichnamigen Ordner.

Nun wechselt ihr in Eclipse (bzw. eure IDE) und aktualisiert mal die Ansicht, ihr solltet nun den neuen Ordner mit den ganzen *.jar Dateien sehen.

Der letzte Schritt besteht darin, die Librarys zu dem Klassenpfad hinzuzufügen. Dazu klickt ihr mit der Rechten Maustaste auf euer Projekt und wählt "Properties", dann wählt ihr rechts "Java Build path", und nun den Tab "Libraries".

Nun klickt ihr auf "Add JARs..." und wechselt in euren lib Ordner. hier wählt ihr alle *.jar Dateien aus und klickt 2x auf "OK". - Das wars auch schon!

Wenn das alles erledigt ist, sollte euer Projektbaum ca so auusehen wie auf dem Bild links.

Die Praxis

Wenn ihr die Installation schon einfach fandet, dann werdet ihr die Implementation lieben! Denn das ist genauso schnell erledigt, denn alle große Arbeiten übernehmen für uns die Libraries, die wir im letzten Schritt importiert haben!

Als erstes benätigen wir eine Klasse deren Objektinstanzen wir dann in die Datenbank schreiben können. Dazu habeich eine einfache kleine Klasse "Car" geschrieben:


import java.io.*;
import java.util.*;

public class Car implements Serializable{
	private static final long serialVersionUID = 1L;
	private String marke;
	private int ps;

	private LinkedList teile = new LinkedList();

	// Konstruktoren

	public Car(){}

	public Car (int ps){
		this.ps = ps;
	}
	public Car(String marke){
		this.marke = marke;
	}
	public Car(String marke, int ps){
		this.marke = marke;
		this.ps = ps;
	}
	public void addTeil(String teil){
		teile.add(teil);
	}

	// Getter &amp;amp; Setter

	public LinkedList getTeile(){
		return teile;
	}
	public void setMarke(String marke){
		this.marke = marke;
	}
	public void setPs(int ps){
		this.ps = ps;
	}
	public String getMarke(){
		return this.marke;
	}
	public int getPs(){
		return ps;
	}
}

Wichtig ist bei dieser kleinen Klasse ist, dass sie das Interface java.io.Serializable implementiert, damit es auch selialisiert werden kann!

Ok nun haben wir ein Objekt, wie krigen wir dieses in die Datenbank? Das geht sehr einfach, wie man an diesem Codeschnippsel sehen kann:


    ObjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        db.set(new Car("VW", 124));
    }
    catch(DatabaseFileLockedException e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Erst wird ein ObjectContainer erstellt, in den wir die Datenbankdatei einlesen, die bei DB4O die Endung yap haben. Wenn diese Datei noch nicht vorhanden ist wird diese automatisch angelegt. Hier gibt es zu beachten, dass die Pfadangabe nicht mit dem Windowstypischen "\" (Backslash) angegebenwird, sondern mit einem normal Slash.

Im folgenden Try-Catch Block wird die Methode Set aufgerufen, der wir auch eine Instanz unseres Car-Objekts übergeben. Das wars auch schon, nun ist diese instanz der Car Klasse schon in der Datenbank gespeichert!

Genauso einfach geht es auch einene Wert aus der Datenbank wieder auszulesen:


bjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        ObjectSet result = db.get(new Car(null));
    }
    catch(Exception e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Dazu wird die Methode Get benutzt, die als Rückgabewert ein ObjectSet mit allen Autos in der Datenbank liefert. Als Parameter wird der Get Methode eine leere Car Klasse übergeben, zu dem Warum komme ich nun.

Abfragesprachen / Abfragemethoden

Wie ihr an dem Letzten Codebeispiel gesehen habt wird hier keine SQL Sprache genutzt um die nötigen Werte aus der Datenbank zu ziehen, sondern es wird mit Objekten gearbeitet. DB4O bietet drei verschiedene Wege um an die gespeicherten Objekte zu kommen:

QBE - Query by Example
SODA - Simple Object Database Access
NQ - Native Queries

Ich werde hier aber nur auf die ersten zwei zusprechen kommen.

QBE bietet die einfachste Möglichkeit an unsere gespeicherten Objekte zu kommen, diese Methode habe ich auch bei dem Beispiel oben genutzt.

Bei dieser methode wird vom Benutzer ein Objekt erzeugt, nach dem dann in der Datenbank gesucht wird. Wenn wir den Fall von eben aufgreifen, wo nur eine leere Car Klasse übergeben wurde, werden wir sehen, dass der Wert null mit allen Werten übereinstimmt. Das heißt, will ich alle Werte aus der Datenbank haben, übergebe ich ihr eine "leere" Klasse.

Wenn ich die Get Abfrage etwas ändere und statt null, "VW" übergebe, würde uns die Datenbank alle Autos ausspucken, die als marke VW haben. Genauso können wir auch nach der PS Zahl suchen, indem wir statt null eine Zahl übergeben. Wir können aber auch, wenn wir genau wissen wonach wir suchen, auch beide Werte übergeben. db.get("VW",120);

Achting, wir können diese methoden nur anwenden, weil wir in der Car Klasse die einzelnen Konstruktoren definiert haben. So muss für jede Suchanfrage auch der passende Konstruktor vorhanden sein. Oder man erstellt einen Konstruktor, mit dem man alles abdecken kann. So könnte man in unserem Beispiel hier auch so vorgehen um alle VW Autos gelistet zu bekommen: db.get("VW", null);

Leider hat diese Vorgehensweise so simpel sie auch ist entscheidende Nachteile. So dürfen in den Konstruktoren keine Werte initialisiert werden, denn wenn ich im Konstruktor die Variable marke standardmäßig als marke = "VW"; initialisieren würde, würde mir die Datenbank bei jeder Suche nur Autos der marke VW ausspucken.

Desweiteren kann man keine Bedingungen benutzen, denn was ist wenn ich zB. alle Autos sehen will, die mehr als 100 PS haben?

Genau hier setzt SODA an. Hier sollte man wissen, dass alle Abfragen in DB4O unter der Haube in SODA laufen, so wird jede Anfrage automatisch konvertiert.

SODA bietet uns hier viele weitere Werkzeuge die man auch aus zB. SQL kennt. So können wir nun auch komplexere Abfragen erstellen. Setzen wir nun mal unser Problem von eben um und fragen alle Autos ab, die mehr als 100 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("ps").constrain(100).greater();
		listResult(q.execute());

Wie man sieht ist diese Abfragemethode etwas komplexer. Aber auf den zweiten Blick ist es aber doch ganz logisch. Zu erst wird ein Query Objekt erzeugt, dem wir dann ein "constrain" (zu Deut. Beschränkung, Begrenzung) übergeben. Diesem übergeben wir "Car.class", somit sagen wir dem Query, dass er nur Objekte vom Typ Car aus der Datenbank holt.

Die nächste Zeile ist eigentlich auch selbsterklärend, dort setzen wir fest, dass die Ergebnisse absteigend (descend) sortiert werden, undzwar nach der Eigenschaft "ps" und durch eine Weitere Beschränkung nur Werte über 100 akzeptiert.

Die letzte Zeile führt den Befehl aus.

Ähnlich kann man auch einen Maximalwert setzen, sodass nur Autos angezeigt werden die zwischen 100 und 130 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		Constraint c1 = q.descend("ps").constrain(100).greater();
		q.descend("ps").constrain(130).smaller().and(c1);
		listResult(q.execute());

Natürlich kann man auch Autos abrufen, die einen bestimmten Wert enthalten. ZB alle Autos die die den Buchstaben w im Namen haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("marke").constrain("w").like();
		listResult(q.execute());

Client / Server umgebung

So, nach der Kurzen Einführung wollen wir nun das eigentlich Ziel umsetzen, nämlich die Datenbank in ein Client Server System integrieren. Zum Glück ist das ebenfalls nicht allzu kompleziert!

Malen wir uns erstmal ein kleines Diagramm um zu verbiltlichen, wie der Aufbau der Programme sein soll:

Bild 2: Der Aufbau

Das Tolle ist, dass die DB4O Libraries und die Arbeit abnehmen eine Verbindugn aufzubauen, die Sockets zu verwalten und die einzelnen Threads anzulegen. Somit müssen wir uns nur auf die Implementierung konzentrieren.

Unsere Server Applikation ist dementsprechend sehr einfach gestrickt:


import com.db4o.*;

public class Server {
	public static void main (String [] args){

		//Server starten mit lokaler Datei
		System.out.println("Starte Server...");
		ObjectServer server = Db4o.openServer("C:/test.yap",54321);

		server.grantAccess("test","pass");
		server.grantAccess("andererUser","pass2");
		try {
			while(true){
				//Leerlauf damit der Server anbleibt
				System.out.println("SERVER: [DB4O] läuft...");
				Thread.sleep(10000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		finally {
			server.close();
		}
	}
}

Wie man sieht sieht man nicht viel. Was auffällt, ist dass man nun statt openFile openServer benutzt wird, und als zweiten Parameter nun einen Port mitgibt, auf dem der Server auf Verbindungen wartet.

Danach werden noch die Benutzer beschrieben, denen der Zugriff auf die Datenbank gewährt werden soll. Und eigentlich wars das auch schon! Die anschließende Schleife sorgt nur dafür, dass das Programm unendlich läuft (zumindest bis der Benutzer es terminiert) und Anfragen von Clients akzeptiert!

Gut nun schauen wir uns mal den Client an:


import com.db4o.*;

public class ClientAdd {

	static ObjectContainer aClient = null;

	public static void main(String [] args){
		aClient = Db4o.openClient("localhost", 54321, "test", "pass");

                //------------------------------------
		Car myCar1 = new Car("Mercedes", 98);
		Car myCar2 = new Car("VW", 158);

		myCar1.addTeil("Dieselmotor");
		myCar1.addTeil("Klimaanlage");
		myCar1.addTeil("ABS");

		myCar2.addTeil("ERP");
		myCar2.addTeil("Schiebedach");
		myCar2.addTeil("Automatik");

		//Sende Objekte
		System.out.println("Sende Objekte...");
		aClient.set(myCar1);
		aClient.set(myCar2);

		aClient.commit();
		System.out.println("Fertig");

		aClient.close();
	}
}

Wie man sieht ist die Verbindung zum Server noch einfacher gestrickt als der Server. Es wird wiedermal ein ObjectContainer instanziert und kann nun genauso benutzt werden wie in den Beispielen von oben.

Ich denke mal das dürfte als Einstieg reichen. Wenn ihr Probleme oder Fragen habt könnt ihr diese hier gern posten, vielleciht kommt dann irgendwann ein zweiter Teil.

Ein Lauffähiges Beispiel gibts hier: Download [Eclipse Project]

Wer mehr erfahren will guckt hier.
Eine wetere gute Anleitung gibts hier.

VN:F [1.9.4_1102]
Rating: 6.7/10 (3 votes cast)
   
Get Adobe Flash playerPlugin by wpburn.com wordpress themes