4

Is there a good way of how to handle soft-delete in Spring Data JDBC?

In Spring Data JPA we can either add @Where(clause="is_active=1") annotation or extend CrudRepository or PagingAndSortingRepository.

Since Spring Data JDBC doesn't support SPEL in queries, we cant write them in generic way like this:

@NoRepositoryBean
public interface SoftDeleteCrudRepository<T extends BasicEntity, ID extends 
Long> extends CrudRepository<T, ID> {
//Override CrudRepository or PagingAndSortingRepository's query method:
@Override
@Transactional(readOnly = true)
@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")
@Transactional(readOnly = true)
public List<T> findAllDeleted(); 

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

So extending CrudRepository or PagingAndSortingRepository means writing same queries for each repository for each entity/table? Like

Repository1
@Override
@Transactional(readOnly = true)
@Query("select id, name, value, deleteFlag from table1 e where e.deleteFlag=false")
public List<T> findAll();
....

Repository2
@Override
@Transactional(readOnly = true)
@Query("select id, name, value, deleteFlag from table2 e where e.deleteFlag=false")
public List<T> findAll();
....

Thank you for the reply in advance!

Dexter
  • 98
  • 7
  • i defined a interface to do this, see mu.xufan answer https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa/54125203#54125203 – mu.xufan Jan 11 '19 at 00:33
  • I don't see how it can delete entity by setting `deleted_ind = true` in DB instead of hard deleting it or retrieve only entities with `deleted_ind = false` for `findBy****` and `findAll` methods – Dexter Jan 11 '19 at 12:38

2 Answers2

2

I currently see three options how one might achieve this.

  1. Use views. For each aggregate root create a database view that filters out the soft-deleted rows. Map your entities against these views.

  2. write your own SqlGenerator. And inject it into the DefaultDataAccessStrategy via a SqlGeneratorSource. Since SqlGenerator is only package-visible you'll have to create your own DefaultDataAccessStrategy for this basically duplicating the existing one. This, of course, will come with a long-term cost of maintaining it.

  3. Since all you seem to need for your scenario is the very special SpEL support for the entity name opening an issue for that and submitting a pull request might be a viable option. If you are interested in this approach and need more help with it mention it in the issue description.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
-1

@Dexter, we use a INT type in db to flag a record's active state, if you use boolean,you can modify StateTag enum(may be interface better), then call changeState to change the state. delete or disable is considered by business service layer,like this:

public class RoleServiceImpl extends SoftDeleteRepositoryServiceImpl<Role, Long>
    implements RoleService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RoleServiceImpl.class);

.......

@Override
public Role deleteRole(Long roleId) {
    return softDelete(roleId);
}

}

and

 public class SoftDeleteRepositoryServiceImpl<T, ID extends Serializable> extends BasicRepositoryServiceImpl<T, ID>
    implements SoftDeleteRepositoryService<T, ID> {


@Override
public T enable(ID id) {
    return updateState(id, ENABLED);
}

  ........

@Override
public T softDelete(ID id) {
    return updateState(id, DELETED);
}
}