使用Spring JPA处理软删除

And*_*ite 47 java spring jpa spring-data spring-data-jpa

我有一个表Stuff定义为......

id, <fields>..., active
Run Code Online (Sandbox Code Playgroud)

Active是软删除标志,总是10.从长远来看,这可能会有利于历史表.

public interface StuffRepository extends JpaRepository<StuffEntity, Long> {} 
Run Code Online (Sandbox Code Playgroud)

在代码中,我们总是 使用活动记录.有没有办法让Spring始终active=1为为此存储库生成的查询附加条件?或者更理想的是允许我扩展用于生成查询的语法?

我知道我可以@queues在任何地方创建命名,但后来我失去了生成的查询的便利性.我还想避免使用"主动"方法污染接口.

我正在使用Hibernate 4.2作为我的JPA实现,如果这很重要的话.

易天明*_*易天明 77

@Where(clause="is_active=1") 不是使用spring数据jpa处理软删除的最佳方法.

首先,它只适用于hibernate工具.

其次,您永远无法使用spring数据获取软删除的实体.

我的解决方案由弹簧数据提供.#{#entityName}表达式可用于通用存储库表示具体实体类型名称.

代码将是这样的:

//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); 
Run Code Online (Sandbox Code Playgroud)

  • 如果我有一个自定义方法,即“findByLastName(String lastName)”或“findByStatus(boolean status)”或“findByAge(intage)”,会发生什么?创建自定义的 `BaseJpaRepostiroy` (我扩展了 JpaRepository)(我使用下面提到的示例)后,我可以删除实体,这意味着在 `findAll()` 或 `findById(int id)` 中只能找到那些实体,其“删除”标志为假。但它不适用于其他自定义的“findByOtherProeprty()”,这会返回“delete”标志也为 true 的所有其他实体。我在这里缺少一些东西来让它们工作吗? (4认同)
  • 如果开发者选择使用Spring数据`@Query`注解写`jpql`怎么办? (2认同)

Sha*_*yam 75

这是一个老问题,你可能已经找到了答案.但是,对于那些寻求答案的所有Spring/JPA/Hibernate程序员来说 -

假设你有一个实体狗:

 @Entity
 public class Dog{

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

 @Column(name="is_active")
 private Boolean active;
 }
Run Code Online (Sandbox Code Playgroud)

和一个存储库:

public interface DogRepository extends JpaRepository<Dog, Integer> {
} 
Run Code Online (Sandbox Code Playgroud)

您需要做的就是在实体级别添加@Where注释,从而产生:

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

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

@Column(name="is_active")
private Boolean active;
}
Run Code Online (Sandbox Code Playgroud)

存储库执行的所有查询都将自动过滤掉"非活动"行.

  • 是的,这是一个Hibernate解决方案.我在答案第一段中提到过,但显然我并不是100%清楚.所以 - 这个解决方案使用了Hibernate的@Where注释.对不起,谢谢你的纠正.通过方式 - 提出问题的人使用休眠(4.2),这是我给出符合他需要的答案的主要原因. (4认同)
  • 在这种情况下,您如何进行删除?从逻辑上讲,所有 JPA 删除都应该是更新查询。相反,使用这种方法,它们将变成`从表中删除,is_active = 1` (4认同)
  • 我相信这是一个以Hibernate为中心的答案.如果您有一些文档显示`@ Where`是JPA或Spring功能,请分享它们. (2认同)

vad*_*shb 27

基于易天明的回答,我用软件删除的覆盖方法创建了CrudRepository实现:

@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();
}
Run Code Online (Sandbox Code Playgroud)

它可以与BasicEntity一起使用:

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

  public abstract Long getId();

  // isActive getters and setters...
}
Run Code Online (Sandbox Code Playgroud)

最终实体:

@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...
}
Run Code Online (Sandbox Code Playgroud)

  • 是否可以将其与PagingAndSortingRepository集成? (3认同)

Oli*_*ohm 10

在当前版本(最高1.4.1)中,Spring Data JPA中没有专门的软删除支持.但是,我强烈建议您使用DATAJPA-307的功能分支,因为这是即将发布的功能.

要使用当前状态更新您使用的版本1.5.0.DATAJPA-307-SNAPSHOT并确保让它引入它需要工作的特殊Spring Data Commons版本.您应该能够按照我们的示例测试用例来了解如何使这些工作正常工作.

PS:我们完成该功能后,我会更新问题.

  • 这是否让它发布? (8认同)
  • 8 年过去了,功能没有更新 - 仍处于设计阶段(捂脸) (3认同)
  • 期待它.你在奥利弗那里做的很棒! (2认同)
  • @VineetBhatia 2020 年 4 月,仍然没有计划何时发布:((基于 Jira 票证) (2认同)