Ordered Lists in JPA – Do it yourself

Bei OneToMany und ManyToMany Relationen gibt es in JPA die Möglichkeit die Sortierreihenfolge festzulegen, mit der die Objekte aus der Datenbank kommen.

@OneToMany
@OrderBy("name ASC")
List<MyEntry> entries;

Dieses Konstrukt sorgt dafür, dass die MyEntry Objekte nach dem darin enthaltenen Feld „text“ in aufsteigender Reihenfolge aus der Datenbank kommen. Das funktioniert soweit auch ganz prima, bis man zu der Liste neue Elemente hinzufügt.

Ist eine Entity gelesen, hat der Programmierer die Verantwortung

Was bedeutet das? In dem Augenblick wo die Entity aus der Datenbank sortiert gelesen wurde, muss sich der Programmierer selber darum kümmern, dass eine neue Entity an der richtigen Stelle in der Liste eingefügt wird. Egal, wie die Cascade Einstellungen sind, egal, wie die Sortiereinstellungen sind. Wird eine Entity außerhalb der Sortierung (also beispielsweise an die letzte Stelle) an die Liste angefügt, bleibt sie da, solange das Objekt sich im internen Cache der JPA Implementation befindet. Das kann, wie versuche gezeigt haben, sehr lange sein. Da hilft kein find, kein close auf den Entity Manager und (zumindest bei Toplink mit Fetchtype.LAZY) auch kein refresh auf der Entity, die die Liste enthält.

Die einfachste Möglichkeit das Problem in den Griff zu kriegen ist, eine add Routine zu implementieren, die einen Comperator entsprechend der von JPA geforderten Sortierreihenfolge zur Verfügung stellt und das Objekt an der richtigen Stelle in die Liste einzufügen:

package jpatest.domain;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;

@NamedQuery(name="MyOrderedTextCollection.findFirst",
    query="select p from MyOrderedTextCollection p")
/**
 *
 * @author tschuett
 */
@Entity
public class MyOrderedTextCollection {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToMany(mappedBy = "collection", cascade=CascadeType.ALL)
    @OrderBy("text ASC")
    private List<MyTextEntry> entries;

    public MyOrderedTextCollection() {
        entries = new ArrayList<MyTextEntry>();
    }

    public Long getId() {
        return id;
    }

    public List<MyTextEntry> getEntries() {
        return Collections.unmodifiableList(entries);
    }

    public void addEntry(String text) {
        if (text == null) {
            throw new NullPointerException(
                "text must not be null");
        }
        MyTextEntry entry = new MyTextEntry(this, text);
        int pos = Collections.binarySearch(entries, entry,
            new Comparator<MyTextEntry>() {
                public int compare(MyTextEntry entry0,
                  MyTextEntry entry1) {
                    return entry0.getText().compareTo(
                      entry1.getText());
            }
        });
        if (pos < 0)
        {
            pos = -(pos +1);
        }
        entries.add(pos, entry);
    }
}

Die Diskussion zu dem Thema lässt sich hier nachlesen:Diskussion in der Glassfish Mailingliste

Das vollständige Beispiel kann hier heruntergeladen werden: jpatest

Java EE 6 – Viele Antworten auf alte Fragen

Java EE 5 war schon ein gewaltiger Schritt in die richtige Richtung. Das gesamte Entwicklungsmodell wurde überarbeitet und wesentlich vereinfacht. Ich würde sogar behaupten, dass Java EE seit der Version 5 überhaupt erst sinnvoll benutzbar ist.

Die Änderungen in Java EE 6 sind nicht ganz so umwälzend aber haben in paar wirklich sinnvolle Features dabei, die ich schon seit langem vermisst habe.

EJB 3.1

Besonders interessant finde ich hier die Einführung von Singletons. Das ist enorm praktisch für beispielsweise Konfigurationen. Ok, es wird im wirklichen Leben wieder erschreckend viele Entwickler geben, die es als JCA Ersatz verwenden – aber das ist ein anderes Thema.

Sehr schön jetzt auch die Möglichkeit der parallelen Ausführung. Damit kann ich z.B. drei Webservices gleichzeitig aufrufen und die Methode kehrt zurück, wenn alle abgearbeitet wurden. Somit dauert der Aufruf nur noch so lange wie der langsamste Service und nicht mehr wie die Summe aller Services.

Endlich wird es auch möglich sein, den Timer Service direkt beim Deployment zu aktivieren – ohne den Umweg über ein Hilfsservlet.

Zusätzlich gibt es noch diverse Vereinfachungen und die möglichkeit den Container embedded zu betreiben – extrem sinnvoll für Tests oder für komplexe Desktopapplikationen.

JPA 2.0

Das was die meisten Menschen freuen dürfte ist „Query by Example“ oder wie es hier heißt „CriteriaAPI“. Damit lassen sich Entities mit Hilfe eines Teilwiese gefüllten Beispielobjekts finden. Datenbankabfragen ganz ohne QL.

Weiterhin kommen ein paar nette Erweiterungen für Embedded Objekte hinzu und das Löschen von Orphants wird möglich sein. (Letzteres konnte zwar jeder JPA Provider war aber nicht standartisiert.)

Spanned sehen auch die Erweiterungen zu Plain Type Collections und Validierungen aus.

JSF 2.0, Servlet 3.0 usw.

Im wesentlichen mehr Komponenten, bessere Ajax Integration und Unterstützung für IO Scheduling „Comet“. Zusätzlich eine Integration von Template artigen Views

Die Aussagen sind noch recht vage und Frontend ist auch nicht so ganz mein Fachgebiet. Ich persönlich würde ein Vorantreiben von JavaFX begrüßen, das es im Bereich Frontend prinzipbedingt mehr Optionen bringen dürfte.