2013-10-11 34 views
28

我有一個表定義爲Stuff ...處理軟刪除與Spring JPA

id, <fields>..., active 

活躍的是軟刪除標誌,並且總是10。長期來看,這可能會取消歷史桌位。

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

在代碼中,我們始終使用的活動記錄。有什麼辦法可以讓Spring始終追加一個active=1條件到爲這個倉庫生成的查詢?或者更理想地允許我擴展用於生成查詢的語法?

據我所知,我可以在任何地方創建名爲@queues,但後來我失去了生成查詢的便利性。我也想避免用「主動」方法污染界面。

我使用Hibernate 4.2作爲我的JPA實現,如果這很重要。

回答

45

這是一個老問題,你可能已經找到了答案。但是,對於所有的春/ JPA/Hibernate的程序員那裏尋求答案 -

假設你有一個實體犬:

@Entity 
public class Dog{ 

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

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

和庫:

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

所有您需要do是在實體級別添加@Where註釋,結果爲:

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

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

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

所有執行的查詢b儲存庫將自動過濾掉「非活動」行。

+2

我相信這是一個以Hibernate爲中心的答案。如果您有一些文檔顯示「@ Where」是JPA或Spring功能,請分享它們。 –

+2

是的,這是一個Hibernate解決方案。我在回答的第一段中提到了它,但顯然我並沒有100%清楚。所以 - 這個解決方案使用了Hibernate的@Where註解。對不起,感謝您的更正。順便說一句 - 問這個問題的人使用hibernate(4.2),這是我給出符合他需求的答案的主要原因。 –

7

在當前版本(最高1.4.1)中沒有對Spring Data JPA中軟刪除的專用支持。不過,我強烈建議您使用DATAJPA-307的功能分支,因爲這是目前正在爲即將發佈的版本開發的功能。

要使用當前狀態更新您使用的版本爲1.5.0.DATAJPA-307-SNAPSHOT,並確保讓它在需要的特殊Spring Data Commons版本中工作。你應該能夠按照樣本test case我們必須看到如何讓這些東西工作。

P.S .:我會在完成該功能的工作後更新問題。

+1

期待它。你在奧利弗那裏做了很棒的工作! –

+7

這是否使它成爲發佈? –

1

如果您不想導入hibernate特定的註釋,我建議您使用數據庫視圖(或Oracle中的等效項)。在mySQL 5.5中,如果過濾條件與活動= 1一樣簡單,則這些視圖可以是可更新和可插入的。1

創建或替換view active_stuff as select * from Stuff where active = 1;

這是一個好主意可能取決於您的數據庫,但它在我的實施中效果很好。

取消刪除需要被訪問的附加實體「東西」直接但那麼將@Where

30

@Where(子句=「IS_ACTIVE = 1」)不是處理軟帶彈簧數據JPA刪除的最佳方式。

首先,它只與hibernate工具一起使用。

其次,您永遠不能使用彈簧數據獲取軟刪除的實體。

我的解決方案是由spring數據提供的。通用存儲庫上可以使用#{#entityName}表達式來表示具體的實體類型名稱。

和代碼將是這樣的:

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

提高您的答案,不確定它爲什麼不在最上面,因爲它以最符合JPA/Spring的方式回答問題。謝謝。 – Max

+0

如果e.id不是「id」,而是「userId」或「accountId」等,這仍然工作,或者我需要將此方法添加到我的所有存儲庫? – cosbor11

+0

SpEL在spring數據中不支持變量現在代表一個id。因此,如果您的實體ID未命名爲ID,請覆蓋這些方法。我想大多數實體都會被稱爲id。 –

1

您可以從SimpleJpaRepository擴展和創建自己的定製庫,您可以在一個通用的方式定義軟delere功能。

您還需要創建一個自定義JpaRepositoryFactoryBean並在您的主類中啓用它。

您可以查看基於我創建CrudRepository實現與重寫的方法軟刪除易天明答案我在這裏https://github.com/dzinot/spring-boot-jpa-soft-delete

8

代碼:

@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(); 
} 

這可能與BasicEntity使用:

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

    public abstract Long getId(); 

    // isActive getters and setters... 
} 

最終實體:

@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

是否有可能將此與PagingAndSortingRepository集成? –

+0

例如,如何覆蓋findAll(Pageable pageable)? – alex