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

2 Gedanken zu „Ordered Lists in JPA – Do it yourself

  1. DaJunkie

    In JPA 2.0 gibt es die Annotation @OrderColumn. Mit dieser verhalten sich persistente Lists wieder genau so, wie man es als Java-Programmierer erwartet 🙂

  2. tschuett Artikelautor

    Danke! Der Artikel ist halt noch aus pre 2.0 Zeiten 😉 Sehr schöne Domain übrigens 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


× 6 = achtzehn