欢迎光临
我们一直在努力

jpa 逻辑删除数据

在 JPA 中,逻辑删除(软删除) 通常通过在实体类中增加一个标记字段(如 isDeleted、deleted、delFlag)来实现。删除时不是物理删除记录,而是将该字段更新为已删除状态,查询时默认过滤掉已删除的数据。


一、基础实现方式

1. 实体类添加逻辑删除字段

java

import javax.persistence.*;

@Entity
@Table(name = "vehicle")
public class Vehicle {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String vin;

private String plateNumber;

// 逻辑删除字段:0-未删除,1-已删除
@Column(name = "is_deleted")
private Integer isDeleted = 0; // 默认值 0

// 或者使用 Boolean
// @Column(name = "is_deleted")
// private Boolean deleted = false;

// getter/setter…
}

2. 自定义逻辑删除方法

java

@Service
public class VehicleService {

@Autowired
private VehicleRepository vehicleRepository;

@Transactional
public void logicDelete(Long id) {
// 方案一:先查询再更新
Vehicle vehicle = vehicleRepository.findById(id)
.orElseThrow(() -> new RuntimeException("记录不存在"));
vehicle.setIsDeleted(1);
vehicleRepository.save(vehicle);
}

@Transactional
public void logicDeleteBatch(List<Long> ids) {
// 批量逻辑删除
List<Vehicle> vehicles = vehicleRepository.findAllById(ids);
vehicles.forEach(v -> v.setIsDeleted(1));
vehicleRepository.saveAll(vehicles);
}
}

3. 查询时过滤已删除数据

java

public interface VehicleRepository extends JpaRepository<Vehicle, Long> {

// 默认查询只返回未删除的
@Query("SELECT v FROM Vehicle v WHERE v.isDeleted = 0")
List<Vehicle> findAllActive();

@Query("SELECT v FROM Vehicle v WHERE v.isDeleted = 0 AND v.id = :id")
Optional<Vehicle> findActiveById(@Param("id") Long id);

// 如果需要查询全部(包括已删除)
@Query("SELECT v FROM Vehicle v")
List<Vehicle> findAllIncludeDeleted();
}


二、使用 @Where 注解(自动过滤)

Hibernate 提供的 @Where 注解可以在所有查询中自动附加过滤条件:

java

import org.hibernate.annotations.Where;

@Entity
@Table(name = "vehicle")
@Where(clause = "is_deleted = 0") // ✅ 所有查询自动加上 is_deleted = 0
public class Vehicle {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String vin;

private String plateNumber;

@Column(name = "is_deleted")
private Integer isDeleted = 0;

// getter/setter…
}

这样写之后,findAll()、findById() 等方法自动只查未删除的数据,无需每次手动加条件。

注意:@Where 是 Hibernate 的注解,不是 JPA 标准,但大多数 JPA 实现(如 Hibernate)都支持。


三、使用 @SQLDelete + @SQLRestriction(更优雅)

java

import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Entity
@Table(name = "vehicle")
@SQLDelete(sql = "UPDATE vehicle SET is_deleted = 1 WHERE id = ?") // 逻辑删除 SQL
@SQLRestriction("is_deleted = 0") // 查询时自动过滤
public class Vehicle {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String vin;

private String plateNumber;

@Column(name = "is_deleted")
private Integer isDeleted = 0;

// getter/setter…
}

这样 repository.delete(entity) 和 repository.deleteById(id) 会自动执行 UPDATE 而不是 DELETE,查询时自动过滤已删除数据。


四、方案对比

方案优点缺点适用场景
手动更新字段 简单直接,无需额外注解 每个删除都要写 update,容易遗漏 简单项目,删除场景少
@Where 自动过滤 查询自动过滤,代码简洁 无法区分"需要查全部"的场景 大多数业务场景
@SQLDelete + @SQLRestriction 最接近物理删除的体验,delete() 自动变 update 需要 Hibernate 支持 希望 delete 方法自动逻辑删除

五、结合 Spring Data JPA 的 @Query 实现

如果不想用 Hibernate 特定注解,可以用 @Query + @Modifying 实现:

java

public interface VehicleRepository extends JpaRepository<Vehicle, Long> {

@Modifying
@Query("UPDATE Vehicle v SET v.isDeleted = 1 WHERE v.id = :id")
int logicDeleteById(@Param("id") Long id);

@Modifying
@Query("UPDATE Vehicle v SET v.isDeleted = 1 WHERE v.id IN :ids")
int logicDeleteByIds(@Param("ids") List<Long> ids);

// 查询时手动过滤
@Query("SELECT v FROM Vehicle v WHERE v.isDeleted = 0")
List<Vehicle> findAllActive();
}

注意:@Modifying 方法需要在 @Transactional 中执行。


六、完整示例

java

@Entity
@Table(name = "vehicle")
@SQLDelete(sql = "UPDATE vehicle SET is_deleted = 1, delete_time = NOW() WHERE id = ?")
@SQLRestriction("is_deleted = 0")
public class Vehicle {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String vin;

private String plateNumber;

@Column(name = "is_deleted")
private Integer isDeleted = 0;

@Column(name = "delete_time")
private LocalDateTime deleteTime;

// getter/setter…
}

// Service 层
@Service
@Transactional
public class VehicleService {

@Autowired
private VehicleRepository repository;

public void delete(Long id) {
repository.deleteById(id); // ✅ 自动执行 UPDATE
// 执行的 SQL: UPDATE vehicle SET is_deleted = 1, delete_time = NOW() WHERE id = ?
}

public List<Vehicle> list() {
return repository.findAll(); // ✅ 自动过滤 is_deleted = 0
}
}


七、总结

需求推荐方案
标准 JPA + 不想加 Hibernate 注解 自定义 @Query + @Modifying
使用 Hibernate + 希望 delete() 自动逻辑删除 @SQLDelete + @SQLRestriction
查询自动过滤已删除数据 @Where(clause = "is_deleted = 0")
需要记录删除时间 在实体类加 deleteTime 字段,更新 SQL 中一起设置
赞(0)
未经允许不得转载:171主机测评 » jpa 逻辑删除数据
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址