Counter Update

I just finished my latest improvements to the legacy version of my counter script.
I just added the lookup for ISPs and added dynamic scaling for the axis legend.
I will now going forward to change the whole system to a more sophisticated software, e.g. using a Datawarehouse approach. The first version of the Data-Model is finished.

So from the old version (that was just some plain tables, flowing around)

 

 

 

 

 

 

 

 

 

 

I created a new Model with more Tables, connected to each other. Basically i devided the Model into a basic Fact (Count) and some Dimensions (for every Value):

 

 

 

 

 

 

 

 

 

 

With my current values (around 22k Facts), i have already to limit the facts that are queryed from the Datebase. I wrote a short script to migrate all old Datasets to the new Data-Model.

Abbilden der Relationen

Nachdem ich alle notwendigen Relationen in der Datenbank abgebildet habe, habe ich jetzt meine Annotations zwischen den Objekten fertig gestellt.
Es gibt ein paar interessante Dinge, so zum Beispiel eine rekursive Relation innerhalb von 2 Tasks (Aufgaben). Das heißt, eine Aufgabe kann n Unteraufgaben haben.


Weiterhin enthält jede Aufgaben ein Array von Empfängern und einen Sender.
Ich sehe grade, dass müsste in dem Fall kein Array sein.
Also ist das eine ManyToOne/OneToMany Beziehung. Werde ich dann noch ändern.

Ich war natürlich wieder einmal vorschnell. Hatte ich ja nicht etwas falsch gemacht
(sowas mache ich ja nicht :D), sondern schlicht etwas vergessen. ich spare mir jetzt
mal, die neuen UMLs hoch zu laden ;-). Letztendlich war die eine ManyToMany
Beziehung zwischen Users und Tasks natürlich die Leute, die Tasks an andere User
weiter delegieren. Das können ja pro Task n sein. Hat alles seine Richtigkeit. Die Welt
steht noch und ich gehe schlafen ^^. Was fehlte war einfach eine
OneToMany/ManyToOne Beziehung zwischen Tasks und Users!
Als letztes kann eine Gruppe (nun habe ich so doch mit rein genommen) 1 bis n Benutzer haben. Genauso kann ein Benutzer in 1 bis n Gruppen sein.
Eine Gruppe wird immer neu erstellt und entsprechender Benutzer hineingepackt, wenn eine Aufgabe mit einem Projekt-Flag (dazu später mehr) zu entsprechendem Benutzer zu geordnet wird. D.h. wenn besagter Benutzer Ersteller oder Verantwortlicher der Aufgabe (und deren Unteraufgaben) ist.
Hier mal das UML der Entitäten:

Wenn Interesse daran besteht, wie man die Abbildung im Einzelnen (im Code) abbildet, kann ich dazu auch noch etwas schreiben.
Und abschließend noch mal eine Übersicht aller Klassen, die ich bislang so habe.
Hierbei steht das E für Entität. Ein S steht für eine Systemklasse. Ein R für eine Klasse für eine einzelne REST Resource und eine R# für eine Resourcenliste.

[PP] ProblemPost

Soooo da das ja bei den Benutzern schon so wunderbar klappt, hier dann mal meine Probleme:
Wie in einem der letzten Posts erwähnt, möchte ich gerne eine Abhängigkeit meiner Entries untereinander haben. Das heißt, ich möchte unter einem Eintrag n Untereinträge haben. Das Klassendiagramm schaut dann so aus:

Meine Tabelle sieht so aus:

Ja eigentlich recht simpel. Nun bietet JPA im Rahmen von Annotation genau eine solche OneToMany Beziehung an.

@OneToMany(cascade=CascadeType.REMOVE)
private List _child_entries = new Vector();

In dieser Liste würden dann alle Kinder des Eintrages stehen.
Da ich mal einfach annehme, dass es unnötig ist, auf DB Ebene noch Foreign Keys zu erstellen, hoffe ich einfach mal, dass das so laufen wird.
Weiterhin habe ich eine Relations Tabelle, die mir die Benutzter mit den Einträgen verbindet und auch sicherstellt, dass n Benutzer für einen Eintrag zuständig sein können:

Hier beginnt es nun problematisch zu werden.
Okay es gibt in dem EJB Buch eine Beschreibung wie man ManyToMany Beziehungen modelliert, allerdings habe ich hier ja auch ein Attribut “status”, welches ja an sich nichts mit der Relation zu tun hat, allerdings schon logisch von dieser Abhängt.
Es würde sich meiner Meinung nach anbieten, dieses als seperates Objekt zu machen.
Ich habe mir das erstmal durch Netbeans generieren lassen. Es ist nun zugegebenermaßen etwas sperrig (weil Netbeans eine eigene PrimaryKey Class drum generieren will, damit sichergestellt ist, dass bei Neuerstellung alle notwendigen Schlüssel belegt sind), allerdings denke ich, dass es eine hinreichende Funktionalität bietet, um erst einmal weiter zu arbeiten.
Kommen wir nun zum Highlight :-/.
Es geht darum, dass die JDBC Anbindung von MySQL seltsamerweise Schwierigkeiten mit Daten zu haben scheint. Siehe dazu auch MySQL Bug #19274.
Ich werde jetzt mal versuchen, eine aktuelle Version zu installieren, die das Problem nicht mehr haben soll. Es geht halt nur darum, dass MySQL mit leeren Datumsangaben (ala 0000-00-00 00:00:00) nichts anfangen kann und eine exception wirft. Das ist in sofern ja nicht schlimm, als das ich einfach einen Eintrag erstellt habe und dann mal hoffe, dass ich damit den Fehler vermeide.
Ich fand weiterhin eine interessante Übersicht, wie JDBC die DB Attribute nach Java überführt:


aus Sun Java System Application Server 9.1 Developer’s Guide

Zu guter Letzte suche ich eine Möglichkeit, um einen klassischen HTTP Redirect (Status 303/301 usw.) Mit der Jersey Umgebung hin bekomme. Es gibt dazu sogar eine Diskussion in dem Blog von Angry Bill unter JSR 311, JAX-RS: Comment and suggestions. Ich fände eine Lösung mit einer @Location Annotation sehr gut. Bisher fand ich nur folgende Lösung, welche ich aber bisher noch nicht ausgiebig testen konnte:
Philosophical content negotiation aus dem Blog von Paul Sandoz (da steht jede Menge zu REST und JSR311 drin)
Abschließend noch eine Bemerkung zu der letzten Diskussion über die Queries.
Im REST-Test von Sun werden u.a. die Pagination-Settings an die URL angehängt also gibt es dann in etwas folgendes als Aufruf:

Request: GET http://localhost:8080/cotodo/resources/users/start=0&max=10&timestamp=1196991596495

Irgendwie schaut das schon wesentlich unübersichtlicher aus. Aber letztendlich muss ich mich eh noch damit befassen, wie ich die Parameter aus der URI extrahiere. Da ich nicht annehme, dass sich die beiden Möglichkeiten großartig unterscheiden werden, kann man sich ja direkt gleich mit der richtigen Lösung befassen.
Ein Problem was ich sehe, ist allerdings die Eindeutigkeit der Parameter, weil diese ja bei steigender Anzahl einen größeren Teil der URI bilden dürften.
Letztendlich ist es aber das gleich Problem wie bei allen Methodenaufrufen, bei denen man anhand eines Aufrufes auch nicht ohne weiteres sagen kann, wofür welcher Wert nun wirklich steht.

JPA YEEHA!

Besser später als nie, hier also dann mal – nach laaaanger Zeit mal wieder – mein aktueller Status.
Mittlerweile bin ich ein ganzes Stück weiter und so fange ich sogar an, dem ganzen System von Persistence etwas abgewinnen zu können. Einzig die Frage bleibt, wieso JPA per default partout alle Feldnamen als groß schreibt. Naja das wird sich sicherlich auch noch klären. Aber zuersteinmal der Reihe nach:
Das – anfängliche Problem, dass ich ja einen Integer Primary Key brauche, aber auch zum Beispiel auch ein User Objekt anhand eines Namens (der ja nach meiner Definition auch eindeutig sein soll) erstellen will, ergab sich die Zwickmühle, dass es ein System geben muss, welche einem quasi einfache SQL Searches erlaubt… gibt es auch.
Anhand einer Präsentation, von Phillip fand sich dann auch eine recht kurze (und demnach schmerzlose) Lösung:

@Entity
@Table(name = "Users")
@NamedQuery(name = "findWithName", 
    query = "SELECT u FROM User u WHERE u.name = :Name")
public class User {
    ...
}

Das sagt der Klasse, dass sie, wenn ich eine Query namens (“findWithName”) ausführe, das nachfolgende SQL Statement benutzen soll und demnach auch den Parameter ‘Name’
Der Aufruf schaut dann so aus:

User user = (User) em.createNamedQuery("findWithName")
            .setParameter("Name", "TestUser").getSingleResult();

Nunja ein schöner, langer Aufruf. Aber immerhin wird mir ein User Objekt erstellt, wenn ein User mit Namen ‘TestUser’ existiert!
Kommen wir zum nächsten Punkt – mein Lieblingspunkt an diesem Morgen, weil das mich ziemlich viel Zeit und Schweizß gekostet hat (obwohls doch eigentlich so einfach ist) – die Kardinalitäten – ODER AUCH -” wie papern, wer n Eintrag an’nen User?”
Ich bin in mich gegangen und kam mit der Entscheidung aus mir (sagt man das so?), dass ich für jeden Eintrag, der von User A nach User B geschickt wird, ein Eintrag in der Datenbank erstellt wird. Also selbst, wenn User A an B und C die gleiche Aufgabe verteilen würde, würden zwei Einträge erstellt. Ich weiß, dass es dann recht unmöglich ist, zum Beispiel zwei User für eine Aufgabe vorzusehen und diese Aufgabe dann bei User B erledigt ist und bei User C noch offen – letztendlich ging es mir auch ersteinmal darum, die Abhängigkeiten hin zu bekommen.
Also erstmal die Veriante mit Stützrädern (zwei Tabellen) und dann hinterher (so morgen) schau ich mal, ob ich das auch freihändig schaffe (so drei Tabellen).
Letztendlich schaut das mit JPA ähnlich aus wie bei Willem und seinen RoR (gabs da nich mal n Lied mit RoR, RoR, RoR the Boat? aber ich glaube das war Row… egal).
Also vielleicht zuerst einmal die beiden Klassen und wie diese zusammenhängen:

Ja die Attribute laden quasi dazu ein, auf eine 3. Tabelle auszulagern ;-).
Es gibt also zwischen User und Entry exact zwei 1-zu-N Beziehungen:
1. Hat ein User genau eine Inbox mit N erhaltenen Entries
2. Hat ein User genau eine Outbox mit N versandten Entries
Da Willem nich Ruhe geben wollte, habe ich die Tabellen gleich mal Pluralisiert (okay ist auch sonst ne ganz gute Idee) und die IDs _id getauft.
Für JPA geht man nun wie folgt vor:

...
public class User {
    ...
    @OneToMany(mappedBy = "recipient")
    private Collection inbox;
    @OneToMany(mappedBy = "sender")
    private Collection outbox;
    ...
}
...

sagt einem, dass jeweils die Attribute (eigentlich die Items aus den Collections) inbox und outbox an das Attribut (und hierbei geht es wohl wirklich um das Objekt ansich) “recipient”/ “sender” gemapped werden.
In der Klasse Entry muss dann natürlich quasi “das Kind konfiguriert werden” (das klingt jetzt seltsam aber im Moment fällt mir nix gescheiteres ein):

...
public class Entry {
    ...
    @ManyToOne()
    @JoinColumn(name="recipient_id")
    private User recipient;
    @ManyToOne()
    @JoinColumn(name="sender_id")
    private User sender;
        ...
    }
...

Na gemerkt? ManyToOne anstatt OneToMany….ach war klar? Na,… okay :-).
Nun werden halt nur noch schnell die Feldnamen aus der DB Table angegeben und besagte Objekte erstellt ( recipient/ sender ). Geht ja eigentlich ganz schnell…
Das Setten/Getten probiere ich dann mal morgen und dann auch gerne mit 1+1+1 = 3 Tabellen. Gibt einem ja dann doch ein wenig mehr Möglichkeiten.
Das EJB3 Buch ist übrigens gut. Man liest, schreibt, sucht dann noch nen anderes Beispiel und dann versteht man es erst ;-). Nach und nachen rücken aber erster und letzter Schritt immer näher zusammen.
Ich wünsche allen eine erfolgreiche Woche.

Betr. JPA

Bisher hat der Umbau der vorhanden Klassen hinsichtlich der Nutzung von JPA promblemloser funktioniert als gedacht. Bisher versuche ich halt nur, das User Objekt aus der Tabelle User auszulesen.
Letztendlich muss ich aber mein bisheriges Konzept soweit umstellen, dass ich die Validierung von Eingaben aus der User Klasse in eine sperate Application Klasse auslagere.
Allen anschein nach klappt das Laden der User Klasse mit Hilfe eines EntityManagers fehlerlos, allerdings scheint es da an einigen Stellen noch zu Problemen zu kommen.
Nach der Überarbeitung sieht die User Klasse nun wie folgt aus:

Die Methoden isLogin and isWithName werden dann wahrscheinlich auch ausgelagert werden müssen. Das Attribut error wird wohl einer Fehlerbehandlungsklasse weichen und in Folge dessen als Sammlung von Exceptions implimentiert werden.
Als Primary Key habe ich mich jetzt für das Attribut Name entschieden, da es scheinbar nur möglich ist, die find Methode des EntityManagers anhand eines Primary keys zu nutzen und ich aller Vorraussicht nach eher nach Namen als nach IDs suchen werde.
Das Laden des Objektes passiert nun in einer neuen Klasse Application:

Leider wird mir bei der Methode showID() eine NullPointerException geworfen, die ich noch nicht ganz nachvollziehen kann :-/. Letztendlich wird mir JPA aber denke ich eine Menge an Arbeit abnehmen, sobald es erstmal stabil läuft.
Bei Sun habe ich allerdings noch ein Beispiel gefunden, was wie perfekt für mich zu sein scheint, weil es schon ziemlich viel von dem behandelt, was ich eigentlich machen möchte [1]

Was bisher so geschieht…

Moin…
Ich wollte mal wieder ein wenig Feedback geben, woran ich bisher so sitze…
Letztendlich bin ich endlich mit einer schönen Datenbankklasse ausgestattet, die mir auch die Dinge bietet, auf die ich angewiesen sein werde – ob da jetzt noch etwas dazu kommt, wird sich zeigen, aber erstmal reicht mir das, was bisher vorhanden ist. Zudem bin ich auf ein paar Besonderheiten von JSP/Java und MySQL gestossen, die in meinen Augen recht unlogisch sind, aber nunja, vielleicht gibt sich das noch.
Mein bisheriger Plan ist es, jetzt erstmal die Grundfunktionen des Servers mit einer einfachen HTML Oberfläche zu erstellen, um dann den Client mit Hilfe von YUI dynamischer zu gestallten.
Aber jetzt erstmall ein Überblick über die Klassen:

Die statische Config Klasse ist erst später hinzugekommen, sodass ich die Attribute aus der Datenbankklasse, welche den Zugriff betreffen wohl dann entferne und die Benutzerdaten zentral in der Config Klasse speichere.
Ich habe jetzt auch schon erste Tests mit einer Session hinter mir, aber das Klassendesign ist noch sehr gewöhnungsbedürftig ^^.

Die Klasse TOOLS soll einfach zur Sammlung verschiedener Funktionen dienen.
Die md5 Methode dient einfach dazu, einen gegeben String als md5 Summe zurückzugeben. Dies brauche ich u.a. für die Passwörter.
Sobald ich die erste HTML Oberfläche habe (die dann einfaches Anmelden, Erstellen und Auflisten von Einträgen beherrschen wird, werd ich erste Funktionstests machen.
Das Schöne, was ich an Eclipse gefunden habe ist, dass man per Vorgabe einfache TextKlassen für Klassen erstellen kann. Dort werden alle Methoden einmal aufgerufen.
Ob das für ein Unit Testing ausreicht muss ich dann noch mal sehen.

Achja… Java und MySQL: Die erhaltenen Einträge aus der Datenbank speichert Java in einem sog. Resultset. Mit der methode next() kann man die verschiedenen Einträge durchblättern. Das komische ist nur, dass wenn – nachweislich – nur ein Eintrag vorhanden ist, muss man dennoch einmal die next() methode aufrufen.
Also muss man von 0. Eintrag auf den 1. blättern? O_o. Meiner Meinung nach irgendwie unnlogisch. Dementsprechend hat mich das auch ein wenig aufgehalten.
Betroffen war u.a. diese Methode aus der Klasse User:

public void load(){
    this.DB.Query("SELECT `ID`, `password` " + 
                   "FROM `users` WHERE `name` = '"+this.name+"' ");
    try {
        ResultSet res = this.DB.getLastResult();
        System.out.println(res.toString());
        res.next();
        this.ID = res.getInt("ID");
        this.password = res.getString("password");
    }catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Ob ich jetzt das “erste Blättern” in der getLastResult() unterbringe habe ich mir noch nicht überlegt.
Wünsche allen frohes Schaffen und ein schönes Rest-Wochenende!