Archiv für die Kategorie ‘Allgemeines’

Datum in Hibernate: Der Wolf(Timestamp) im Schafspelz(Date)

Freitag, 22. Januar 2010

Benutzt man Hibernate als OR-Mapper und will ein java.util.Date (Datum und Uhrzeit) speichern, ist Vorsicht geboten. Da java.sql.Timestamp von java.util.Date erbt, und der Typ in der Datenbank i.d.R. timestamp ist, bekommt man von Hibernate gegebenenfalls ein Timestamp-Objekt, und nicht ein Date-Objekt. Eigentlich kein Problem, doch die Klasse Timestamp ist …kaputt! Auszug aus dem Javadoc von Timestamp:

The Timestamp.equals(Object) method never returns true when passed a value of type java.util.Date because the nanos component of a date is unknown.

Will man zwei Dates vergleichen und befindet sich hinter der ersten Referenz ein Timestamp-Objekt, schlägt equals fehl.

Date d = new Date();
Date ts = new Timestamp(d.getTime());
System.out.println(ts.equals(d));    //returns false

Wie soll ich also zwei Dates miteinander vergleichen? Es wird noch schlimmer:

System.out.println(ts.compareTo(d)); // return 0
System.out.println(d.compareTo(ts)); // returns 1

Auch die compareTo-Methode ist fehlerhaft liefert ein anderes Ergebnis als erwartet. Schaut man sich den Konstruktor von Timstamp an, kommt man aus dem Staunen Entsetzen nicht mehr heraus. Die Klasse Date speichert das Datum millisekundengenau in dem Feld fastTime. Die Klasse Timestamp hingegen speichert das Datum sekundengenau in dem Feld fastTime und die Milli- und Nanosekunden im Feld nanos. Warum dies so ist, und nicht nur die Nanosekunden im Feld nanos gespeichert werden, ist mir ein Rätsel.

Will man nun ein Date und einen Timestamp miteinander vergleichen (z.B. auch mit before() und after()) schlägt dies fehl. Die compareTo-Methode von Date vergleicht die Millisekunden aus dem Feld fastTime, beim Timestamp-Objekt ist dies nur sekundengenau, die Millisekunden sind im Feld nanos gespeichert, und werden nicht berücksichtigt.

Dieses Problem tritt häufig in Verbindung mit Hibernate auf, die Schuld liegt aber bei der Standard Java-API. Allerdings lässt sich das Problem umgehen, indem man Hierbernate mit einem UserType zwingt, nur Objekte vom Typ Date zu liefern.

public class DateTimeType implements UserType {
    private static final int[] SQL_TYPES = new int[] {Types.TIMESTAMP};
    public DateTimeType() {
        super();
    }
 
    @SuppressWarnings("unchecked")
    public Class returnedClass() {
        return java.util.Date.class;
    }
 
    public int[] sqlTypes() {
        return SQL_TYPES;
    }
 
    public boolean equals(final Object x, final Object y) {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        Date xDate = (Date) x;
        Date yDate = (Date) y;
        return xDate.equals(yDate);
    }
 
    public Object nullSafeGet(final ResultSet rs, final String[] names, final Object owner) throws SQLException {
        // extract Timestamp from the result set
        Timestamp timestamp = (Timestamp) Hibernate.TIMESTAMP.nullSafeGet(rs, names[0]);
        // return the value as a java.util.Date (dropping the nanoseconds)
        if (timestamp == null) {
            return null;
        } else {
            return new Date(timestamp.getTime());
        }
    }
 
    public void nullSafeSet(final PreparedStatement st, final Object value, final int index) throws SQLException {
        // handle the NULL special case immediately
        if (value == null) {
            st.setTimestamp(index, null);
            return;
        }
        // make sure the received value is of the right type
        if (!Date.class.isAssignableFrom(value.getClass())) {
            throw new IllegalArgumentException("Received value is not a [java.util.Date] but [" + value.getClass() + "]");
        }
        // set the value into the resultset
        Timestamp tstamp = null;
        if (value instanceof Timestamp) {
            tstamp = (Timestamp) value;
        } else {
            tstamp = new Timestamp(((Date) value).getTime());
        }
        st.setTimestamp(index, tstamp);
    }
 
    public Object deepCopy(final Object value) {
        if (value == null) {
            return null;
        } else {
            return ((Date) value).clone();
        }
    }
 
    public boolean isMutable() {
        return true;
    }
 
    @Override
    public Object assemble(final Serializable cached, final Object owner) {
        return deepCopy(cached);
    }
 
    @Override
    public Serializable disassemble(final Object value) {
        return (Date) value;
    }
 
    @Override
    public int hashCode(final Object x) {
        return x.hashCode();
    }
 
    @Override
    public Object replace(final Object original, final Object target, final Object owner) {
        return deepCopy(original);
    }
}

Möglich ist eine Konfiguration auf Packet-Ebene, indem man eine package-info.java erstellt. Folgend die Konfiguration mit Annotationen auf Klassenebene:

@TypeDefs( {@TypeDef(name = "dateTimeType", typeClass = DateTimeType.class)})
public class MyBusinessObject {
 
    @Type(type = "dateTimeType")
    @Column(name = "time")
    private Date time;
    ...
}

Eventuell wird die freie Joda Time API in Java 1.7 die alten Datum und Kalendarklassen ersetzten. Bis dahin kann man mit diesem UserType sicher sein, dass Hibernate nur Date-Objekte zur Verfügung stellt, und somit kann man wieder equals und compareTo ohne Bedenken verwenden.
Siehe auch


Jan Kuenstler


Buchvorstellung “Payback”

Freitag, 15. Januar 2010

Früher hatte dieser Blog ja mal den Untertitel “Technologie und ihre Folgen”. In diesem Sinne erlaube ich mir mal zur Abwechslung ein Buch vorzustellen, dass mir zum Jahreswechsel zwischen die Finger und unter die Augen gekommen ist.

Schirrmacher - PaybackEs handelt sich um das Buch “Payback” von Frank Schirrmacher.  Erschienen im Karl Blessing Verlag (16. November 2009).

Der Autor beschäftigt sich darin vorrangig mit der Frage, welche Auswirkungen die IT-Entwicklungen der letzten Jahre, insbesondere Vernetzung, WWW und Google, auf uns als Menschen haben. Dabei legt der Autor großen Wert darauf, dass er  kein grundsätzlicher Technologieskeptiker ist, der alle neuen Entwicklungen grundsätzlich ablehnt. Es geht auch nicht um Ablehnung, sondern vielmehr um kritische Beobachtung der Dinge, die um uns herum passieren und denen wir nicht entkommen können.

Warum ist dieses Buch nun etwas für diesen Blog? Wie der Autor ganz richtig bemerkt sind wir, die Softwareentwickler und Architekten, diejenigen, die mehr als jeder Andere Einfluss haben auf die Gestaltung unserer Welt. Hört sich massiv an, aber ist es nicht so? Wir sind die paar Prozent der Gesellschaft, die einerseits die technischen Rahmenbedinungen des WWW überhaupt verstehen aber auch ausbauen können. Also kann es ja nicht schaden, intelligente Gedanken zu diesem Thema zu hören und zu reflektieren.

Intelligente Gedanken sind ein gutes Stichwort: Das Buch ist definitiv nix für die intellektuelle Unterschicht. Bei amazon findet man Kommentare, dass der Autor zu viel schwallert und wichtigtuerisch fehlerhafte Thesen aufstellt. Der Sprachstil ist gepflegt gehoben und der Mann schreibt eben, wie ein Germanist schreibt. Aber man muss sagen: für einen Germanisten hat er erstaunlich viel verstanden.

Seine Beobachtungen von Rahmenbedingungen und Zusammenhängen finde ich recht interessant, auch wenn die eine oder andere Schlussfolgerung sicher in Frage zu stellen ist. Insbesondere zum Ende hin erwartet der Leser eine Art Lösungsvorschlag zu den angesprochenen Themen, der aber deutlich schwammiger (und rein von der Anzahl der Seiten im Buch kleiner) ausfällt, als die Feststellung der Ist-Situation. Somit bleibt ein etwas schaler Nachgeschmack, zumindest für mich als lösungsorientierten Menschen.

Unterm Strich möchte ich dieses Buch dennoch jedem empfehlen, der mal eine andere Sichtweise auf unsere tägliche Arbeit und unsere Welt gewinnen möchte. Ich habe viele bekannte Aspekte in dem Buch wiedergefunden und hatte Spaß an einem teils kritischen, teils interessanten und teils philosophischen Ausflug. Wie gesagt: für einen Germanisten hat er eine Menge verstanden.


Andreas Jene


Eclipse Shortcuts Teil 2

Freitag, 08. Januar 2010

Eclipse verfügt über unzählige Shortcuts für nahezu alle Funktionen und Befehle. Jedes mal wenn ich auf der Suche nach neuen Shortcuts war, stieß ich auf lange unüberschaubare Listen. Es waren einfach zu viele neue Befehle um sich etwas zu merken. Neue Befehle habe ich fast nur durch abgucken gelernt. Auf diese Weise lerne ich nur einen neuen Shortcut, der mich dann auch gerade interessiert. Was bringen mir 100 Shortcuts, wenn ich vorher immer überlegen bzw. 2 mal probieren muss um den richtigen zu treffen. Da bleibe ich dann lieber “Mausschubser”.

Für diese Liste habe ich nun den Vormittag über jeden Shortcut, den ich benutzt habe in eine Datei getippt. Für diese Befehle brauche ich nicht nachdenken. Für alles andere verwende ich dann lieber die Maus.

STRG + d – Entfernen einer ganzen Zeile.

ALT + UP/DOWN – Verschieben der aktuellen Zeile nach oben oder unten.

ALT + LEFT/RIGHT – Springen zwischen den letzten bearbeiteten Zeilen.

STRG + 1 -Zeigt für die aktuelle Zeile die möglichen “quick fix” optionen an (z.b. Variable zuweisen, umbennen usw.). Für die meisten Optionen gibt es auch direkte Shortcuts aber die brauche ich mir so nicht merken. Nur durch diesen Shortcut ist diese Liste so kurz geblieben.

STRG + (SHIFT) + j – Suche im aktuellen Editor ohne Dialog. Diese Suche funktioniert ähnlich der Suche im Firefox. Nach dem drücken von STRG + J einfach das gesuchte Wort zu tippen. Im Editor wird dann nächsten möglichen Treffer gesprungen. Mit Shift wird die Suchrichtung geändert. Wiederholtes drücken spring zum nächsten möglichen Treffer.

STRG + SHIFT + t – Direktes öffnen einer Klasse. Neben ganzen Klassennamen können im Suchfeld auch nur die Großbuchstaben einer Klasse eingeben werden, wenn diese einen “Camel Case” Namen trägt (z.B. MTK für “MeineTestKlasse”, wenn es mehrere Treffer gibt dann funktioniert auch “MTKlas”).

STRG + SHIFT + r – Ähnlich der Suche einer Klasse, nur für beliebige Dateien.

STRG + SHIFT + g – “Java Search” für den markierten Code. Sehr hilfreich um Stellen zu finden, an denen eine Klasse verwendet oder eine Methode aufgerufen wird.

STRG + SPACE – Der Klassiker, kennt wohl wirklich jeder. Vervollständigung einer Methode, Klasse oder Variablen.

STRG + o – Zeigt innerhalb einer geöffneten Klasse alle darin befindlichen Methoden an, um dorthin springen zu können. Durch wiederholtes drücken können zusätzlich die geerbten Methoden angezeigt werden.

STRG + t – Zeigt auf einer Klasse die Vererbungshirarchie an, bzw. auf einer Methode die ggf. überschriebenen Methoden der Oberklassen an.

STRG + SHIFT + F – Formatieren der geöffneten Datei. Ist dank der Save Action nicht mehr wirklich notwendig. Ich brauche es nur noch für XML Dateien, da bei mir die Save Actions dort nicht ausgeführt werden.

STRG + 7 – Text aus/einkommentieren.

ALT + SHIFT + R – Umbennen einer Klasse Variablen oder Methode.

Speichern, also STRG + s braucht man ja eigentlich nicht mehr erwähnen. Bei vielen Entwicklern die ich kenne ist STRG + s ein Reflex und kein bewußter Befehl. Copy & Past benutze ich natürlich fast nie beim Entiwckeln ;-) .


Felix Breske


Schnellere Web-Anwendungen

Montag, 04. Januar 2010

Bei der Entwicklung von Web-Anwendungen ist die Performance in der Regel die letzte Frage, die gestellt wird. Der Fokus liegt im Regelfall auf der Umsetzung der Anforderungen.

Die Geschwindigkeit der Anwendung sollte aber früh mit eingeplant werden, da die Antwortzeiten darüber entscheiden, ob eine Anwendung als gut oder schlecht wahrgenommen wird. Dauert das Laden des gewünschten Inhalts zu lange, wird die Anwendung nie die Akzeptanz der Benutzer erfahren. Spontane Benutzer können sogar die Anwendung verlassen, weil sie die Anwendung / den Link als tot betrachten. Wer wartet schon 15 Sekunden auf eine Web-Seite!?

Die Geschwindigkeit einer Anwendung wird durch viele Parameter bestimmt. Während Anwendungen serverseitig meistens aufgrund einer hohen Anzahl von Datenbankabfragen lahmen, ist das Problem clientseitig komplexer, lässt sich aber auf 2 grundlegende Aussagen zusammenfassen:

  • Größe der geladenen Objekte (HTML, Bilder, CSS, JavaScript)
  • Anzahl der eingebundenen Objekte (Bilder, CSS, JavaScript)

Die Größe der Objekte bedingt eine längere Ladezeit und die Anzahl der Objekte führt zu einer Vielzahl von Anfragen an den Server. Jede Anfrage an den Server erzeugt einen eigenen Overhead.
Erst wenn alle Objekte vollständig geladen sind, kann der Inhalt vollständig und korrekt angezeigt werden.

Schritt 1: Komprimierung
Die Größe fast jeden Inhalts kann durch eine Komprimierung verringert werden. Die Komprimierung erfolgt am besten durch den Webserver. Beim Apache kann dies das Modul mod_deflate übernehmen.

Schritt 2: Caching
Das Meiste, was für die Anzeige der Oberfläche einer Web-Anwendung benötigt wird, kann gecached werden. Einzig die dynamisch generierten Objekte (im Regelfall nur das HTML), dürfen nicht gecached werden.
Der Server kann den Client durch durchgängiges Setzen von Header-Informationen unterstützen, die angeben wie lange das Objekt gültig ist. Auch hierfür gibt es beim Apache ein entsprechendes Modul: mod_expires

Mit diesen beiden rudimentären Schritten läßt sich die Performance schon spürbar verbessern, da weniger Objekte vom Server übertragen werden und diese nicht größer als notwendig sind.
Dies ist natürlich noch lange nicht alles, sollte aber bei geringem Aufwand den größten Nutzen erzielen.

Zur Unterstützung der Optimierung einer Webseite kann ich Firebug und YSlow (2 Plugins für Firefox) empfehlen.
Während man mit Firebug die einzelnen Anfragen an den Server mit den Headern für den Serverrequest und -response einsehen kann, gibt YSlow weitergehende Tipps zur Optimierung einer HTML-Seite.

Geschwindigkeit ist nicht alles, aber: “Performance rules!”


Thomas Bader