使用Spring JPA处理软删除

时间:2022-09-11 16:27:31

I have a table Stuff defined as...

我把桌子的东西定义为......

id, <fields>..., active

Active is the soft-delete flag and is always 1 or 0. Long term this may go away in favor of a historical table.

Active是软删除标志,始终为1或0.长期来看,这可能会有利于历史表。

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

In code, we always use active records. Is there any way to get Spring to always append an active=1 condition to queries generated for this repository? Or more ideally allow me to extend the grammar used to generate the queries?

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

I understand that I can create named @queues everywhere but then I lose the convenience of the generated queries. I also want to avoid polluting the interface with "active" methods.

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

I am using Hibernate 4.2 as my JPA implementation if that matters.

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

6 个解决方案

#1


52  

This is an old question, and you probably already found the answer. BUT, for all the Spring/JPA/Hibernate programmers out there seeking for answer -

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

Say you have an entity Dog:

假设你有一个实体狗:

 @Entity
 public class Dog{

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

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

and a repository:

和一个存储库:

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

All you need to do is add the @Where annotation on the entity level, resulting:

您需要做的就是在实体级别添加@Where注释,结果是:

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

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

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

All the queries performed by the repository will automatically filter out the "non-active" rows.

存储库执行的所有查询都将自动过滤掉“非活动”行。

#2


45  

@Where(clause="is_active=1") is not the best way to handle soft delete with spring data jpa.

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

First, it only works with hibernate implement.

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

Second, you can never fetch soft deleted entities with spring data.

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

My solution is el provided by spring data. #{#entityName} expression can be used on generic repository represent concrete entity type name.

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

And code will be like this:

代码将是这样的:

//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); 

#3


16  

Based on 易天明 answer I've created CrudRepository implementation with overriden methods for soft delete:

根据易天明的回答,我用软件删除的覆盖方法创建了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();
}

It could be used with BasicEntity:

它可以与BasicEntity一起使用:

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

  public abstract Long getId();

  // isActive getters and setters...
}

And final entity:

最终实体:

@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...
}

#4


8  

In current versions (up to 1.4.1) there's no dedicated support for soft deletes in Spring Data JPA. However, I strongly encourage you to play with the feature branch for DATAJPA-307 as this is a feature currently worked on for the upcoming release.

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

To use the current state update the version you use to 1.5.0.DATAJPA-307-SNAPSHOT and make sure you let it pull in the special Spring Data Commons version it needs to work. You should be able to follow the sample test case we have to see how to get that stuff working.

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

P.S.: I'll update the question once we finished working on the feature.

P.S。:一旦我们完成了该功能,我将更新问题。

#5


2  

You can extend from SimpleJpaRepository and create your own custom repository where you can define the soft delere functionality in a generic way.

您可以从SimpleJpaRepository扩展并创建自己的自定义存储库,您可以在其中以通用方式定义软删除功能。

You'll also need to create a custom JpaRepositoryFactoryBean and enable that in your main class.

您还需要创建一个自定义JpaRepositoryFactoryBean并在主类中启用它。

You can check my code here https://github.com/dzinot/spring-boot-jpa-soft-delete

你可以在这里查看我的代码https://github.com/dzinot/spring-boot-jpa-soft-delete

#6


1  

I suggest you use a database view (or equivalent in Oracle) if you don't want to import hibernate specific annotations. In mySQL 5.5, these views can be updateable and insertable if the filter criteria is as simple as active=1

如果您不想导入特定于hibernate的注释,我建议您使用数据库视图(或Oracle中的等效视图)。在mySQL 5.5中,如果过滤条件与active = 1一样简单,则这些视图可以更新和插入

create or replace view active_stuff as select * from Stuff where active=1;

创建或替换视图active_stuff作为select * from Stuff where active = 1;

Whether this is a good idea probably depends on your database but it works great in my implementation.

这是否是一个好主意可能取决于您的数据库,但它在我的实现中工作得很好。

Undeleting required an additional entity which accessed 'Stuff' directly but then so would @Where

取消删除需要一个额外的实体,直接访问'Stuff'但是@Where会这样

#1


52  

This is an old question, and you probably already found the answer. BUT, for all the Spring/JPA/Hibernate programmers out there seeking for answer -

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

Say you have an entity Dog:

假设你有一个实体狗:

 @Entity
 public class Dog{

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

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

and a repository:

和一个存储库:

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

All you need to do is add the @Where annotation on the entity level, resulting:

您需要做的就是在实体级别添加@Where注释,结果是:

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

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

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

All the queries performed by the repository will automatically filter out the "non-active" rows.

存储库执行的所有查询都将自动过滤掉“非活动”行。

#2


45  

@Where(clause="is_active=1") is not the best way to handle soft delete with spring data jpa.

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

First, it only works with hibernate implement.

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

Second, you can never fetch soft deleted entities with spring data.

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

My solution is el provided by spring data. #{#entityName} expression can be used on generic repository represent concrete entity type name.

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

And code will be like this:

代码将是这样的:

//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); 

#3


16  

Based on 易天明 answer I've created CrudRepository implementation with overriden methods for soft delete:

根据易天明的回答,我用软件删除的覆盖方法创建了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();
}

It could be used with BasicEntity:

它可以与BasicEntity一起使用:

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

  public abstract Long getId();

  // isActive getters and setters...
}

And final entity:

最终实体:

@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...
}

#4


8  

In current versions (up to 1.4.1) there's no dedicated support for soft deletes in Spring Data JPA. However, I strongly encourage you to play with the feature branch for DATAJPA-307 as this is a feature currently worked on for the upcoming release.

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

To use the current state update the version you use to 1.5.0.DATAJPA-307-SNAPSHOT and make sure you let it pull in the special Spring Data Commons version it needs to work. You should be able to follow the sample test case we have to see how to get that stuff working.

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

P.S.: I'll update the question once we finished working on the feature.

P.S。:一旦我们完成了该功能,我将更新问题。

#5


2  

You can extend from SimpleJpaRepository and create your own custom repository where you can define the soft delere functionality in a generic way.

您可以从SimpleJpaRepository扩展并创建自己的自定义存储库,您可以在其中以通用方式定义软删除功能。

You'll also need to create a custom JpaRepositoryFactoryBean and enable that in your main class.

您还需要创建一个自定义JpaRepositoryFactoryBean并在主类中启用它。

You can check my code here https://github.com/dzinot/spring-boot-jpa-soft-delete

你可以在这里查看我的代码https://github.com/dzinot/spring-boot-jpa-soft-delete

#6


1  

I suggest you use a database view (or equivalent in Oracle) if you don't want to import hibernate specific annotations. In mySQL 5.5, these views can be updateable and insertable if the filter criteria is as simple as active=1

如果您不想导入特定于hibernate的注释,我建议您使用数据库视图(或Oracle中的等效视图)。在mySQL 5.5中,如果过滤条件与active = 1一样简单,则这些视图可以更新和插入

create or replace view active_stuff as select * from Stuff where active=1;

创建或替换视图active_stuff作为select * from Stuff where active = 1;

Whether this is a good idea probably depends on your database but it works great in my implementation.

这是否是一个好主意可能取决于您的数据库,但它在我的实现中工作得很好。

Undeleting required an additional entity which accessed 'Stuff' directly but then so would @Where

取消删除需要一个额外的实体,直接访问'Stuff'但是@Where会这样