Search code examples
spring-boothibernatejpajoinsoft-delete

Soft deletion not propagating to child entities in Spring Boot JPA


I want to implement Soft Delete in my Spring Boot project which has @OneToOne and @OneToMany relations. Whenever I soft delete a parent entity, the soft delete should propagate to the child entities too. Additionally, I also want to store the deletion reason in the Parent entity. I have tried with the code below but the soft deletion doesn't propagate to the child entities. I can't used @SQLDelete either since I have two parameters in my delete operation.

@Entity
@Table(name="personal_details")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class PersonalDetail extends Auditable<String> implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private String idNo;

    private String gender;

    private String name;

    // If the record has been soft-deleted
    private Boolean deleted;

    // Storing the reason why the record was deleted
    private String deletionReason;

    // For Address
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id_no", referencedColumnName = "idNo")
    private Address address;

    // For Devices Received
    @JsonManagedReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "personalDetail", cascade = CascadeType.ALL)
    private List<DevicesReceived> devicesReceivedList;

    // For Devices Required
    @JsonManagedReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "personalDetail", cascade = CascadeType.ALL)
    private List<DevicesRequired> devicesRequiredList;

    // For Benefits
    @JsonManagedReference
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "personalDetail", cascade = CascadeType.ALL)
    private List<Benefit> benefitList;
}
@Entity
@Table(name = "address")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Address extends Auditable<String> implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    private String idNo;

    private String address;

    // If the record has been soft-deleted
    private Boolean deleted;
}
@Entity
@Table(name="devices_received")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class DevicesReceived extends Auditable<String> implements Serializable {
    private static final long serialVersionUID = 1L;

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

    private String idNo;

    private String device;

    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "personal_details_id_no", nullable = false)
    private PersonalDetail personalDetail;

    // If the record has been soft-deleted
    private Boolean deleted;
}
@Repository
public interface PersonalDetailRepository extends JpaRepository<PersonalDetail, String> {

    @Transactional
    @Modifying
    @Query(nativeQuery = true, value = "UPDATE personal_details SET deleted = TRUE, deletion_reason = (?2) WHERE id_no = (?1);")
    void delete(String idNo, String deletionReason);

}

Solution

  • In terms of Hibernate your deletion is not deletion, but just update, so it doesn't have to propagate this operation to the associated entities. I recommend you to consider using Hibernate's out-of-box soft delete.

    Also, don't use Lombok's @Data for entities, it implicitly generates equals/hashcode which use fields-associations and may cause unexpected behavior.