2013-10-11 15 views
28

Ich habe eine Tabelle Stuff definiert als ...Umgang mit Soft-Löschungen mit Feder JPA

id, <fields>..., active 

Aktiv ist der Soft-löschen-Flag und ist immer 1 oder 0. Auf lange Sicht kann dies zugunsten einer historischen Tabelle wegfallen.

public interface StuffRepository extends JpaRepository<StuffEntity, Long> {} 

In Code, wir immer Verwendung aktive Aufzeichnungen. Gibt es eine Möglichkeit, Spring zu veranlassen, immer eine active=1-Bedingung an Abfragen anzuhängen, die für dieses Repository generiert werden? Oder idealerweise erlaube ich mir, die Grammatik zu erweitern, mit der die Abfragen generiert wurden?

Ich verstehe, dass ich überall den Namen @queues erstellen kann, aber dann verliere ich den Komfort der generierten Abfragen. Ich möchte auch vermeiden, die Schnittstelle mit "aktiven" Methoden zu verschmutzen.

Ich verwende Hibernate 4.2 als meine JPA-Implementierung, wenn das wichtig ist.

Antwort

45

Dies ist eine alte Frage, und Sie haben wahrscheinlich schon die Antwort gefunden. ABER, für alle Frühjahr/JPA/Hibernate Programmierer gibt für Antwort suchen -

Angenommen, Sie haben ein Unternehmen Hund:

@Entity 
public class Dog{ 

......(fields)....   

@Column(name="is_active") 
private Boolean active; 
} 

und ein Repository:

public interface DogRepository extends JpaRepository<Dog, Integer> { 
} 

Alles, was Sie brauchen, um Sie ist es, die @Where Anmerkung auf der Unternehmensebene hinzufügen, was:

@Entity 
@Where(clause="is_active=1") 
public class Dog{ 

......(fields)....   

@Column(name="is_active") 
private Boolean active; 
} 

die Abfragen All durchgeführt b y Das Repository filtert automatisch die "nicht aktiven" Zeilen heraus.

+2

Ich glaube, dies ist eine Hibernate zentrische Antwort. Wenn Sie Dokumente haben, die anzeigen, dass '@ Where' eine JPA- oder Spring-Funktion ist, teilen Sie sie bitte mit. –

+2

Ja, das ist eine Hibernate-Lösung. Ich habe es in der Antwort ersten Absatz erwähnt, aber anscheinend war ich nicht 100% klar. Also - diese Lösung verwendet Hibernates @Where Annotation. Entschuldigung und danke für die Korrektur. Auf dem Weg - die Person, die die Frage stellte, verwendet Hibernate (4.2), was der Hauptgrund für mich war, eine Antwort zu geben, die seinen Bedürfnissen entspricht. –

7

In aktuellen Versionen (bis 1.4.1) gibt es keine dedizierte Unterstützung für Soft-Löschungen in Spring Data JPA. Ich rate Ihnen jedoch dringend dazu, mit dem Feature-Zweig für DATAJPA-307 zu spielen, da dies eine Funktion ist, die derzeit für die kommende Veröffentlichung bearbeitet wird.

Um den aktuellen Status zu verwenden, aktualisieren Sie die Version, die Sie verwenden, um 1.5.0.DATAJPA-307-SNAPSHOT und stellen Sie sicher, dass Sie in der speziellen Spring Data Commons-Version ziehen, die es funktionieren muss. Sie sollten in der Lage sein, dem Beispiel test case zu folgen, wir müssen sehen, wie man das Zeug zum Laufen bringt.

S.S .: Ich werde die Frage aktualisieren, sobald wir mit der Arbeit an der Funktion fertig sind.

+1

Ich freue mich darauf. Du machst tolle Arbeit dort Oliver! –

+7

Hat dies zur Veröffentlichung geführt? –

1

Ich empfehle Ihnen, eine Datenbankansicht (oder eine Entsprechung in Oracle) zu verwenden, wenn Sie keine Hibernate-spezifischen Anmerkungen importieren möchten. In mySQL 5.5 können diese Ansichten aktualisierbar und einschiebbar sein, wenn die Filterkriterien so einfach wie aktiv = 1

erstellen oder ersetzen Ansicht active_stuff als select * from Stuff wo aktiv = 1;

Ob dies eine gute Idee ist, hängt wahrscheinlich von Ihrer Datenbank ab, aber es funktioniert gut in meiner Implementierung.

Gelöschte eine zusätzliche Einheit benötigt, die ‚Stuff‘ direkt zugegriffen, aber dann würde

30

@Where (Klausel = „is_active = 1“) so @Where ist nicht der beste Weg, mit Federdaten JPA zu handhaben weich zu löschen.

Erstens funktioniert es nur mit Hibernate implementieren.

Zweitens können Sie nie gelöschte Entitäten mit Federdaten abrufen.

Meine Lösung wird von Federdaten zur Verfügung gestellt. Der Ausdruck # {# entityName} kann im generischen Repository als konkreter Entitätstypname verwendet werden.

Und Code wird wie folgt sein:

enter code here 

//Override CrudRepository or PagingAndSortingRepository's query method: 
@Override 
@Query("select e from #{#entityName} e where e.deleteFlag=false") 
public List<T> findAll(); 

//Look up deleted entities 
@Query("select e from #{#entityName} e where e.deleteFlag=true") 
public List<T> recycleBin(); 

//Soft delete. 
@Query("update #{#entityName} e set e.deleteFlag=true where e.id=?1") 
@Modifying 
public void softDelete(String id); 
+0

Verbessern Sie Ihre Antwort, nicht sicher, warum es nicht an der Spitze ist, denn es beantwortet die Frage in der meisten JPA/Spring-freundlichen Art und Weise. Vielen Dank. – Max

+0

Was ist, wenn e.id nicht "id" ist, sondern "userId" oder "accountId" usw., funktioniert das noch oder muss ich diese Methode zu allen meinen Repositories hinzufügen? – cosbor11

+0

SpEL im Frühjahr Daten unterstützt keine Variable jetzt eine ID darstellen. Überschreiben Sie diese Methoden also, wenn Ihre Entitäts-ID keine ID-ID hat. Der größte Teil Ihrer Entität wird id heißen, denke ich. –

1

Sie können von SimpleJpaRepository erweitern und Ihre eigenen Repository erstellen, in dem Sie die Soft-delere Funktionalität in allgemeiner Weise definieren können.

Sie müssen auch eine benutzerdefinierte JpaRepositoryFactoryBean erstellen und diese in Ihrer Hauptklasse aktivieren.

Sie können meinen Code hier überprüfen https://github.com/dzinot/spring-boot-jpa-soft-delete

8

Basierend auf 易天明 Antwort, die ich CrudRepository Umsetzung mit überschriebene Methoden für weiche löschen erstellt haben:

@NoRepositoryBean 
public interface SoftDeleteCrudRepository<T extends BasicEntity, ID extends Long> extends CrudRepository<T, ID> { 
    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.isActive = true") 
    List<T> findAll(); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.id in ?1 and e.isActive = true") 
    Iterable<T> findAll(Iterable<ID> ids); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.id = ?1 and e.isActive = true") 
    T findOne(ID id); 

    //Look up deleted entities 
    @Query("select e from #{#entityName} e where e.isActive = false") 
    @Transactional(readOnly = true) 
    List<T> findInactive(); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select count(e) from #{#entityName} e where e.isActive = true") 
    long count(); 

    @Override 
    @Transactional(readOnly = true) 
    default boolean exists(ID id) { 
     return findOne(id) != null; 
    } 

    @Override 
    @Query("update #{#entityName} e set e.isActive=false where e.id = ?1") 
    @Transactional 
    @Modifying 
    void delete(Long id); 


    @Override 
    @Transactional 
    default void delete(T entity) { 
     delete(entity.getId()); 
    } 

    @Override 
    @Transactional 
    default void delete(Iterable<? extends T> entities) { 
     entities.forEach(entitiy -> delete(entitiy.getId())); 
    } 

    @Override 
    @Query("update #{#entityName} e set e.isActive=false") 
    @Transactional 
    @Modifying 
    void deleteAll(); 
} 

Es könnte mit BasicEntity verwendet werden:

@MappedSuperclass 
public abstract class BasicEntity { 
    @Column(name = "is_active") 
    private boolean isActive = true; 

    public abstract Long getId(); 

    // isActive getters and setters... 
} 

Und endgültige Einheit:

@Entity 
@Table(name = "town") 
public class Town extends BasicEntity { 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "town_id_seq") 
    @SequenceGenerator(name = "town_id_seq", sequenceName = "town_id_seq", allocationSize = 1) 
    protected Long id; 

    private String name; 

    // getters and setters... 
} 
+2

ist es möglich, dies mit PagingAndSortingRepository zu integrieren? –

+0

Wie würden Sie zum Beispiel Seite findAll (Pageable pageable) überschreiben? – alex