6 Stimmen

JPA - "versionierte" Entität, Gestaltungshinweise erforderlich

Stellen Sie sich die folgenden zwei Einheiten vor. Element ist eine einfache Klasse, die einige Daten enthält:

@Entity
public class Element {
    private String data;

    public String getData() { return data; }    
    public void setData(String data) { this.data = data; }
}

Nächste Klasse, genannt VersionedElement , erweitert Element und enthält verschiedene Versionen zusammen mit der aktuellen Version. Hier ist meine "Lösung":

@Entity
public class VersionedElement extends Element {
    private Set<Element> versions;
    private Element currentVersion;

    @Override
    public String getData() {
        return getCurrentVersion().getData();
    }

    @Override
    public void setData(String data) {
        getCurrentVersion().setData(data);
    }

    @OneToMany
    public Set<Element> getVersions() {
        return versions;
    }

    public void setVersions(Set<Element> versions) {
        this.versions = versions;
    }

    @ManyToOne
    public Element getCurrentVersion() {
        return currentVersion;
    }

    public void setCurrentVersion(Element currentVersion) {
        this.currentVersion = currentVersion;
    }
}

Und mir gefällt nicht, was ich geschrieben habe, irgendetwas stimmt da nicht, ein zu geradliniger Ansatz. Zunächst einmal, in der letzten Klasse currentVersion ist nicht begrenzt durch und hat keine Beziehung zu versions . Es sieht so aus, als ob dem Code einige Hilfsklassen, eine Abstraktionsebene, eine JPA-Annotationstechnik oder alles zusammen fehlen. Ich brauche eine elegante, der JPA-Handbuchlösung würdige Lösung für diesen einfachen Fall. Für Hinweise, Links oder Codeschnipsel wäre ich dankbar.

13voto

Lucas de Oliveira Punkte 1612

Wenn Sie eine sofort einsatzbereite Lösung für die Versionierung von Hibernate-Entitäten suchen, versuchen Sie hibernate-envers. Damit wird die Versionierung/Auditierung von Objekten ein Kinderspiel für Sie. Lesen Sie die Dokumentation unter http://docs.jboss.org/envers/docs/index.html

Prost und viel Glück!

4voto

CMR Punkte 986

Element kann ein ganzzahliges Feld haben version im Objekt Element selbst, die als laufende Zählung von Zeilen fungiert, und wird durch eine Sequenz aktualisiert. Wenn Sie das Neueste wollen, müssen Sie einfach nach diesem Feld in absteigender Reihenfolge ordnen und das erste Ergebnis abrufen.

@Entity
@NamedQueries({
    @NamedQuery(name="GetHistory", query = "FROM Element e WHERE e.id = :id"),
    @NamedQuery(name="GetLatest", query = "FROM Element e \
                                      WHERE e.id = :id order by e.version"),
})
public class Element {
    private String data;

    @GeneratedValue(strategy = GenerationType.SEQUENCE, 
                    generator = "SEQ_ELEMENT_VERSION")
    private int version;
    private int id;

    public String getData() { return data; }    
    public void setData(String data) { this.data = data; }
}

2voto

Ioan Alexandru Cucu Punkte 11629

Ihre Lösung würde funktionieren, aber mit einer weiteren Tabelle für die VersionedElement wäre ein Performance-Overhead: VersionedElement würde außer einigen Fremdschlüsselspalten keine nützlichen Daten enthalten.

Ich würde einfach Folgendes hinzufügen Aktuelles Element als Feld zu Klasse Element . Dann würde ich in der DAO einige Methoden hinzufügen, die Abfragen auf der Grundlage dieses Feldes durchführen:

List<Element> getHistory(Element element)...
Element getLatest(Element element)...

JPA unterstützt auch die @Version-Annotation, aber diese wird für die optimistische Gleichzeitigkeitskontrolle verwendet. Für die Verfolgung von Versionsnummern kann sie dennoch verwendet werden.

2voto

Arthur Ronald Punkte 32423

Nun, ich habe Ihren Kommentar zur Antwort von @Ioan Alexandru Cucu gesehen

Ich erwarte, dass dies irgendwie von der Sql-Seite kompensiert wird. weniger betroffene Zeilen, optimierte Abfrage

Gemäß der in Ihrer Frage dargestellten Zuordnung, wenn Sie eine vollständig initialisierte VersionedElement-Entität abrufen müssen müssen Sie eine Abfrage wie die folgende durchführen

from
    VersionedElement v
inner join fetch
    v.versions
inner join fetch
    v.currentVersion
where
    v.id = :id

Wie Sie sehen können, benötigen Sie zwei Verknüpfungen, um Ihre VersionedElement-Entität abzurufen . Aber sowohl Element als auch VersionedElement die Dateneigenschaft teilen . Um doppelten Code zu vermeiden, können wir eine abstrakte Klasse definieren, die die in beiden Entitäten benötigten Daten enthält, wie folgt

@MappedSuperclass
public abstract class AbstractElement {

    private String data;

    public String getData() { return data; }
    public void setData(String data) { this.data = data; }

}

Die JPA 1.0-Spezifikation ist einfach gehalten

Sowohl abstrakte als auch konkrete Klassen können Entitäten sein. Entitäten kann Nicht-Entität erweitern Klassen als auch Entitätsklassen, und Nicht-Entitätsklassen können Entitätsklassen erweitern.

Wir brauchen die @MappedSuperclass-Anmerkung, weil ihre Zuordnungsinformationen s auf die Entitäten angewandt werden, die von ihr erben . In unserem Fall: Element und VersionedElement.

Wir können also die Entität Element umschreiben als

@Entity
public class Element extends AbstractElement {}

Und zur Vermeidung des inner join fetch v.currentVersion Warum speichern Sie die von AbstractElement gelieferten Daten nicht als @Embedded-Eigenschaft anstelle einer @ManyToOne-Eigenschaft?

@Embeddable
public class ElementAsEmbeddable extends AbstractElement {}

@Entity
public class VersionedElement {

    private ElementAsEmbeddable currentElement;

    private Set<Element> versions;

    @Embedded
    public ElementAsEmbeddable getCurrentVersion() { return currentVersion; }
    public void setCurrentVersion(ElementAsEmbeddable currentVersion) { this.currentVersion = currentVersion; }

    @OneToMany
    public Set<Element> getVersions() { return versions; }
    public void setVersions(Set<Element> versions) { this.versions = versions; }

}

Ihre Abfrage sollte nun wie folgt aussehen

from
    VersionedElement v
inner join fetch
    v.versions
where
    v.id = :id

Nur eine Verbindung

Um die Eigenschaft currentVersion von einem Element aus einzurichten, müssen Sie das Element einfach als AbstractElement

versionedElement.setCurrentVersion((AbstractElement) element);

0voto

ThomasRS Punkte 8187

An Ihrer Lösung ist nichts auszusetzen, aber vielleicht sollten Sie die Entity-Hörer um den Zustand Ihrer Entitäten auf elegantere Weise sicherzustellen, ich denke mit @prePersist und @preUpdate Listenern. Oder wählen Sie alternativ eine Liste anstelle eines Sets.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X