Soft-Delete(软删除)—Spring Data JPA学习笔记(5)
在一些业务场景中,需要用到软删除Soft-Delete。所谓软删除就是记录并未真实从数据库中删除,而是通过某种技法,把删除的记录隐藏起来,使得查询时无法获取“删除的”记录。常用的做法是设定删除标志值,然后通过标志值进行过滤。当然,也可以设计一个删除表,将删除的记录转存到删除表中。
- Spring Data JPA实现Soft-Delete(软删除)
查阅了一些资料发现在JPA的层面来实现Soft-Delete较为麻烦,大都需要在Repository层重载实现。而在JPA的实现层hibernate、eclipse link都有比较简单的实现方法。这次要不看下hibernate是怎么实现软删除的
hibernate的实现软删除还是很方便的,只需在定义实体的时候添加注解@SQLDelete、@Where。
@SQLDelete是指实体删除时,执行的SQL语句。
@Where是指查询是附加的查询条件
@SQLDelete(sql = "UPDATE sys_user SET deleted=true WHERE id=?")
@Where(clause = "deleted = false")
@Entity
public class SysUser extends BaseIntegerEntity { private String username;
private String email;
@Enumerated(EnumType.STRING)
private GenderType gender;
@Column(nullable = false)
private Boolean deleted = Boolean.FALSE;
//get set
}
查询时执行的SQL语句,会自动附加@Where的查询条件。
执行删除操作时,则会执行@SQLDelete的SQL语句
常用的Repository通过@Query定义JPQL或者SQL是否也可以自动附加查询条件呢?在SysUserRepository新增3个方法
public interface SysUserRepository extends BaseRepository<SysUser, Integer> {
@Query(value = "SELECT * FROM sys_user WHERE gender='MALE'" ,nativeQuery = true)
List<SysUser> findMalesBySQL();
@Query(value = "SELECT u From SysUser u Where u.gender='MALE' ")
List<SysUser> findMales();
List<SysUser> findSysUsersByGender(GenderType gender);
}
执行findSysUserByGender的SQL语句
执行findMales的SQL语句
执行findMalesBySQL的SQL语句,理所当然是不可以的,但万一呢?好吧,没有万一……
- 包含关联关系的软删除
独立的实体已经达到了软删除的目的,那如果有关联关系会怎样。
修改下实体SysUser,然后新增实体Organization
@SQLDelete(sql = "UPDATE sys_user SET deleted=true WHERE id=?")
@Where(clause = "deleted <> true")
@Entity
public class SysUser extends BaseIntegerEntity {
private String username;
private String email;
@Enumerated(EnumType.STRING)
private GenderType gender;
@Column(name = "organization_id")
private Integer organizationId;
@JoinColumn(name = "organization_id",insertable = false,updatable = false)
@ManyToOne(fetch = FetchType.LAZY)
private Organization organization;
@Column(nullable = false)
private Boolean deleted = Boolean.FALSE;
//get set
@SQLDelete(sql = "UPDATE organization SET deleted=true WHERE id=?")
@Where(clause = "deleted <> true")
@Entity
public class Organization extends BaseIntegerEntity {
private String name;
private String code;
@OneToMany(cascade = CascadeType.REMOVE,fetch = FetchType.LAZY, mappedBy = "organization")
private List<SysUser> users;
@Column(nullable = false)
private Boolean deleted = Boolean.FALSE;
//get set
}
通过Organization服务访问下users
执行的SQL语句,已经自动附加了SysUser附加条件
来试下级联删除有没有问题,可以简单执行SQL的语句,也是SQLDelete自定义的。
- @Filter和@FilterDef过滤器
通过注解@SQLDelete和@Where已经实现软删除大部分功能了。但是如果要根据业务可以动态设置是否过滤“删除的”记录,要如何实现呢?这里要用到hibernate提供另外的两个注解@Filter和@FilterDef。我们修改实体SysUser如下
@SQLDelete(sql = "UPDATE sys_user SET deleted=true WHERE id=?")
@Filter(name = "softDeleteFilter", condition = "deleted= :deleted")
@FilterDef(name = "softDeleteFilter",
parameters = @ParamDef(name = "deleted", type = "boolean"),
defaultCondition = "deleted <> true")
@Entity
public class SysUser extends BaseIntegerEntity {
private String username;
private String email;
@Enumerated(EnumType.STRING)
private GenderType gender;
@Column(name = "organization_id")
private Integer organizationId;
@JoinColumn(name = "organization_id", insertable = false, updatable = false)
@ManyToOne(fetch = FetchType.LAZY)
private Organization organization;
@Column(nullable = false)
private Boolean deleted = Boolean.FALSE;
//get set
}
过滤器已经定义好了,要起作用的话,需要手动启用下
@Service
public class SysUserServiceImpl extends BaseCommonCrudApplicationServiceImpl<SysUser, Integer, SysUserDTO, SysUserRepository>
implements SysUserService {
private final EntityManager entityManager;
protected SysUserServiceImpl(SysUserRepository repository, EntityManager entityManager) {
super(repository);
this.entityManager = entityManager;
@Override
public List<SysUserDTO> getMaleUserList() {
List<SysUser> users;
try (Session session = entityManager.unwrap(Session.class)) {
Filter softDeleteFilter = session.enableFilter("softDeleteFilter");
softDeleteFilter.setParameter("deleted", true);