Handling soft-deletes with Spring JPA
Solution 1:
@Where(clause="is_active=1")
is not the best way to handle soft delete with spring data jpa.
First, it only works with hibernate implement.
Second, you can never fetch soft deleted entities with spring data.
My solution is el provided by spring data. #{#entityName}
expression can be used on generic repository represent concrete entity type name.
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);
Solution 2:
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 -
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:
@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.
Solution 3:
Based on 易天明 answer I've created CrudRepository implementation with overriden methods for soft delete:
@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:
@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...
}
Solution 4:
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.
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.
P.S.: I'll update the question once we finished working on the feature.
Solution 5:
You can extend from SimpleJpaRepository and create your own custom repository where you can define the soft delere functionality in a generic way.
You'll also need to create a custom JpaRepositoryFactoryBean and enable that in your main class.
You can check my code here https://github.com/dzinot/spring-boot-jpa-soft-delete