Wie mache ich eine Entität schreibgeschützt?

74

Was ist der richtige Weg, um eine Entität mit JPA schreibgeschützt zu machen? Ich möchte, dass meine Datenbanktabelle niemals programmgesteuert geändert wird.

Ich denke, ich verstehe, dass ich meine Objekte mit sperren sollte LockModeType.READ. Ist es möglich, eine Anmerkung zu verwenden, um meine Entitäten nach dem Abrufen aus der Datenbank direkt zu sperren? Oder muss ich mein generisches DAO für diese bestimmte Entität herumspielen und überschreiben?

glmxndr
quelle

Antworten:

46

Eine Lösung besteht darin, feldbasierte Anmerkungen zu verwenden, Ihre Felder als zu deklarieren protectedund nur öffentliche Getter vorzuschlagen. Dadurch können Ihre Objekte nicht verändert werden.

(Diese Lösung ist nicht entitätsspezifisch, sondern nur eine Möglichkeit, unveränderliche Objekte zu erstellen.)

Nicolas
quelle
18
Sie haben vergessen: "und stellen Sie sicher, dass die Getter nur unveränderliche Werte zurückgeben". zB Sammlungsfelder
Daniel Alexiuc
Was bedeutet feldbasierte Annotation?
MyTitle
1
Es funktioniert, aber ich hasse es. Das Testen ist unnötig schwierig, da Sie sich etwas einfallen lassen müssen, um die Kapselung zu unterbrechen. Ich schlage eine benutzerdefinierte Annotation vor, die eine Ausnahme verursacht, wenn eine Setter-Methode aufgerufen wird und kein Testframework erkannt wird. Diese Anmerkung würde auch in Ihrer IDE wie @Deprecated aussehen. Alternativ können Sie eine Anmerkung als Erweiterung Ihres bevorzugten Testframeworks schreiben.
user447607
5
Sie sollten privat statt geschützt verwenden. Wenn Sie es schützen, können Sie weiterhin von anderen Klassen im selben Paket auf die Eigenschaft zugreifen. Instanzen von Faustregelvariablen sind immer privat.
Jordan Silva
3
Mit diesem Ansatz können Sie noch neue Entitäten (INSERT) hinzufügen
andy
48

Fügen Sie in Ihrer Entität Folgendes hinzu EntityListener:

@Entity
@EntityListeners(PreventAnyUpdate.class)
public class YourEntity {
    // ...
}

Implementieren Sie Ihre EntityListener, um eine Ausnahme auszulösen , wenn ein Update auftritt:

public class PreventAnyUpdate {

    @PrePersist
    void onPrePersist(Object o) {
        throw new IllegalStateException("JPA is trying to persist an entity of type " + (o == null ? "null" : o.getClass()));
    }

    @PreUpdate
    void onPreUpdate(Object o) {
        throw new IllegalStateException("JPA is trying to update an entity of type " + (o == null ? "null" : o.getClass()));
    }

    @PreRemove
    void onPreRemove(Object o) {
        throw new IllegalStateException("JPA is trying to remove an entity of type " + (o == null ? "null" : o.getClass()));
    }
}

Dadurch wird ein kugelsicheres Sicherheitsnetz für Ihr Unternehmen mit JPA-Lifecycle-Listenern erstellt.

  • PRO: JPA-Standard - nicht spezifisch für den Ruhezustand
  • PRO: sehr sicher
  • CON: Zeigt nur Schreibversuche zur Laufzeit an . Wenn Sie eine Überprüfung der Kompilierungszeit wünschen , sollten Sie keine Setter implementieren.
Slartidan
quelle
Ich sehe das einige Jahre später und es ist immer noch eine großartige Lösung. :)
drognisep
28

Der Ruhezustand enthält auch eine org.hibernate.annotations.ImmutableAnmerkung, die Sie dem Typ, der Methode oder dem Feld hinzufügen können.

Toby Artisan
quelle
10
Sei vorsichtig mit @Immutable. Es ist nicht sehr hilfreich. Wenn Sie versuchen, ein Update durchzuführen, tritt kein Fehler auf: Es schlägt nur stillschweigend fehl . Aus der Dokumentation von Hibernate: "Aktualisierungen einer unveränderlichen Entität werden ignoriert, es wird jedoch keine Ausnahme ausgelöst."
David Lavender
2
Sie können die Entität auch mit @Immutable kommentieren. In unserem Fall war ein stiller Fehler erforderlich, da wir bestimmte Entitäten aktualisieren wollten, andere jedoch nicht. Diese sollten stattdessen mithilfe gespeicherter Prozeduren aktualisiert werden.
Kt Mack
24

Wenn sich Ihre JPA-Implementierung im Ruhezustand befindet, können Sie die Annitation "Entity im Ruhezustand" verwenden

@org.hibernate.annotations.Entity(mutable = false)

Dies wird Ihr Modell natürlich an den Ruhezustand binden.

Andrew B.
quelle
53
Die Entität ist im Ruhezustand von jpa2 veraltet. Verwenden Sie stattdessen @Immutable
Juan Rojas
15

IIRC Sie könnten jedes Feld auf insertable = falseund updatable = falsein Ihren @ColumnAnmerkungen setzen, aber ich bin sicher, dass es eine bessere Methode geben muss ... :)

Ich nehme nicht an, dass das hilft?

Mac
quelle
13

Ich denke, was Sie suchen, ist Ihre Entität, unveränderlich zu sein. Der Ruhezustand unterstützt dies; JPA (mindestens JPA 1.0) nicht. Ich nehme an, Sie können dies nur steuern, indem Sie nur Getter bereitstellen und sicherstellen, dass die Getter nur unveränderliche Werte zurückgeben.

DaTroop
quelle
12

Die Eclipselink-Implementierung bietet Ihnen auch die @ReadOnlyAnnotation auf Entitätsebene

Pau
quelle
wo die Referenz könnte ich , dass ich nicht auf Juni 2016 jetzt finden
shareef
8

Dies wird mich wahrscheinlich abstimmen, weil ich immer abgelehnt werde, weil ich es vorgeschlagen habe, aber Sie können AspectJ auf verschiedene Arten verwenden, um dies durchzusetzen:

Automatisieren Sie entweder die Mac-Lösung (lassen Sie AspectJ die @ColumnAnmerkung einfügen):

declare @field : (@Entity *) *.* : @Column(insertable=false);

Oder deklarieren Sie einen Compilerfehler für alle Zugriffe auf festgelegte Methoden:

declare error : execution((@Entity *) *.set*(*) );

Nachteil: Sie müssen Ihrem Build eine AspectJ-Kompilierung hinzufügen, aber das ist einfach, wenn Sie Ant oder Maven verwenden

Sean Patrick Floyd
quelle
Wenn Sie einen Compilerfehler für alle Zugriffe auf festgelegte Methoden deklarieren, warum deklarieren Sie diese?
Nicolas
2
Damit das Framework sie verwenden kann Der Compilerfehler gilt nur für lokalen Code. Der Code des JPA-Anbieters wird normalerweise nicht unter Verwendung des Aspekts gewebt
Sean Patrick Floyd,
Also ... kann ein Client, der Entitäten über ein Jar verwendet, auf Setter zugreifen?
Nicolas
@Nicolas Mit dem declare errorAnsatz ja. Dies funktioniert nur, wenn der aufrufende Code dem AspectJ-Compiler zur Verfügung steht. Der erste Ansatz funktioniert jedoch trotzdem, da er die tatsächlichen Entity-Klassendateien ändert.
Sean Patrick Floyd
Nur damit du weißt, es ist nicht nützlich für mich, aber ich habe trotzdem gestimmt. Nicht weil Sie sich beschwert haben, sondern weil es tatsächlich für jemand anderen nützlich sein kann. :) Danke, dass du das erwähnt hast.
glmxndr
5

Wenn Sie Spring-Daten verwenden oder anderweitig das Repository-Muster verwenden, fügen Sie für diese bestimmte Entität keine Methoden zum Speichern / Aktualisieren / Erstellen / Einfügen / usw. in das Repository ein. Dies kann verallgemeinert werden, indem eine Basisklasse / Schnittstelle für schreibgeschützte Entitäten und eine aktualisierbare Klasse vorhanden ist, die die schreibgeschützte für aktualisierbare Entitäten erweitert. Wie andere Poster bereits betont haben, können die Setter auch nicht öffentlich gemacht werden, um zu vermeiden, dass Entwickler versehentlich Werte festlegen, die sie dann nicht speichern können.

JavaMan
quelle