数据库的逻辑删除及其JPA实现方案

本文详细介绍了如何在Spring JPA和Hibernate中使用@SQLDelete和@Where注解实现逻辑删除,包括其原理、应用场景、示例以及如何避免查询到逻辑删除的记录。了解如何在数据安全性与冗余之间找到平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

什么是逻辑删除#

为什么需要逻辑删除#

使用spring-jpa和 hibernate的@SQLDelete和@Where注解实现逻辑删除

使用 Hibernate 进行逻辑删除

更新当前 session 中的删除状态

查询时避开逻辑删除的记录

示例

总结

Reference


什么是逻辑删除#

所谓逻辑删除是指数据已经“不需要”了,但是并没有使用delete语句将这些数据真实的从数据库中删除,而只是用一个标志位将其设置为已经删除。

为什么需要逻辑删除#

对数据进行逻辑删除,一般存在以下原因:

  • 防止数据误删除,不能找回数据;

  • 这些数据还具有一定的商业价值,比如用户的注册信息;

  • 虽然这些数据可以删除,但是这些数据还有关联数据,这些关联数据不能删除。

对数据进行逻辑删除,可以保证数据的安全性和完整性。但是,逻辑删除也会带来的一些问题:

  • 数据库表的数据冗余,导致查询缓慢;

  • 写sql进行数据处理时需要排除那些已经逻辑删除的数据,这就会导致sql复杂,容易出错,特别是涉及多表查询时;

  • 进行逻辑删除时,还需要考虑与之相关的数据怎么处理;

  • 还有,如果数据表的某个字段要求唯一,并强制约束,比如用户表中的登录用户名字段,设计为逻辑删除的话,一旦有新的同用户名记录就无法插入。但如果不将该字段设置为唯一性约束的,那么在每次插入数据的时候,都需先进行一次查询,看看有无未(逻辑)删除的同名记录存在,低效率是一回事,而且在高并发的系统中,很难保证其正确性

所以是否需要对数据进行逻辑删除,需要根据具体的业务场景,以及逻辑删除的优缺点进行综合考虑。

网友的一些建议

综合考虑,对于中小型的项目,逻辑删除所带来的好处有限,但带来的问题却很多。如果平时做好数据备份工作,还是可以预防物理删除隐患的。但心里应该清除,当项目大到一定程度,对数据安全性的要求高到一定程度,使用逻辑删除代替物理删除是必然的,在后面的数据库设计中,可以先小范围的尝试使用逻辑删除,一旦开发模式成熟,就全面使用逻辑删除代替物理删除。

使用spring-jpa和 hibernate的@SQLDelete和@Where注解实现逻辑删除

转载自:JPA / Hibernate 的逻辑删除 | Prophet's Blog (prophet-xu.com)

另外参考:使用hibernate的@SQLDelete和@Where注解实现逻辑删除wangming1473的博客-CSDN博客hibernate 逻辑删除

使用 Hibernate 进行逻辑删除

进行逻辑删除时,需要覆盖 Hibernate 默认的删除操作,告诉 Hibernate 使用特定的 UPDATE 语句来代替 DELETE。这一操作可以使用 @SQLDelete 注解来进行。

使用者需要为 @SQLDelete 注解给定 sql 字段。Hibernate 在执行删除操作,生成原生 SQL 时,会使用该字段。因此,该字段需要是数据库原生的 SQL。之后,不论是使用 Spring Data Jpa 提供的 SimpleJpaRepository 还是使用 @Query 注解来写 JPQL来执行删除操作,Hibernate 都会使用用户指定的 sql 来代替默认的 delete 语句。当然,用户写的原生 SQL 是不会进行该替换的。

更新当前 session 中的删除状态

由于 @SQLDelete 的原理是在生成 SQL 语句的时候对默认的 delete 语句进行替换,因此,Hibernate 并不知道他所管理的 Entity 的相应字段在执行过删除操作后进行了改变。通常情况下,这并没有什么问题,因为只要 Hibernate 执行了逻辑删除的 SQL,新的实体在查询的时候就会感知到删除状态。然而,在某些情况下,如执行了 EntityManager.remove(Object entity) 后,并没有立即 flush 到数据库,并立即对该 entity 进行操作,那么此时 entity 的相应字段并不会变为删除状态。解决这种情况的方法就是通过 @PreRemove 注解来注册一个回调函数,在该函数中更新相应字段的状态。这样,Hibernate 在执行删除之前,会回调该方法,从而更新相应字段的状态。

查询时避开逻辑删除的记录

执行逻辑删除后,通常不希望在执行查询操作时查询到逻辑删除过的记录,Hibernate 给出的解决方案是通过 @Where 注解。@Where 注解类似于 @SQLDelete 注解,用户在这里提供自定义的原生 where 语句,Hibernate 在生成真正的 SQL where 语句时,会将该语句与目标语句的查询条件通过 and 进行连接。用户可以将未逻辑删除的限定条件通过该注解进行设定,之后,不论是 JPQL 还是 EntityManager 相关的查询操作,都会带上该条件。

示例

本部分给出一个简单的示例,如下所示。User 的 deleted 域标明了该实体的删除状态。通过 @SQLDelete 注解指定删除操作时要执行的 update 操作,通过 @Where 注解指定了查询时的拼接条件。通过 @PreRemove 注解指定执行删除操作时的状态变更操作。

import lombok.Data;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.*;
​
@Data
@Entity
@SQLDelete(sql = "update user set deleted = 1 where id = ?")
@Where(clause = "deleted = 0")
public class User {
​
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
​
    private String name;
​
    /**
     * 逻辑删除标志位,0 未删除,1 已删除
     */
    private Integer deleted;
​
    @PreRemove
    public void deleteUser() {
        this.deleted = 1;
    }
}

在执行删除时,Hibernate 生成的语句如下:

Hibernate: update user set deleted = 1 where id = ?

执行查询操作时,Hibernate 生成语句如下:

Hibernate: select user0_.id as id1_0_0_, user0_.deleted as deleted2_0_0_, user0_.name as name3_0_0_ from user user0_ where user0_.id=? and ( user0_.deleted = 0)

总结

借助 Hibernate 提供的 @SQLDelete 注解、@Where 注解,可以方便的实现 JPA 的逻辑删除。配合 Spring Data JPA 等优秀的开源项目,我们可以更加灵活方便的使用 JPA。

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值