spring boot实现软删除
本文中,我们只给了部分示例代码。
如果你需要完整的代码,请点击:https://github.com/mengyunzhi/springBootSampleCode/tree/master/softDelete。
本文开发环境:spring-boot:2.0.3.RELEASE
+ java1.8
WHY TO DO
软删除:即不进行真正的删除操作。由于我们实体间的约束性(外键)的存在,删除某些数据后,将导致其它的数据不完整。比如,计算机1801班的教师是张三,此时,我们如果把张三删除掉,那么在查询计算机1801班时,由于张三不存了,所以就会报EntityNotFound的错误。当然了,在有外键约束的数据库中,如果张三是1801班的教师,那么我们直接删除张三将报一个约束性的异常。也就是说:直接删除张三这个行为是无法执行的。
但有些时候,我们的确有删除的需求。比如说,有个员工离职了,然后我们想在员工管理中删除该员工。但是:该员工由于在数据表中存在历史记录。比如我们记录了17年第二学期的数据结构是张三教的。那么,由于约束性的存在,删除张三时就会报约束性错误。也就是说:出现了应该删除,但却删除不了的尴尬。
这就用到了本文所提到的软删除,所谓软删除,就是说我并不真正的删除数据表中的数据,而是在给这条记录加一个是否删除的标记。
spring jpa
是支持软删除的,我们可以找到较多质量不错的文章来解决这个问题。大体步骤为:1. 加入@SqlDelete("update xxxx set deleted = 1 where id = ?")
。2.加入@Where(clause = "deleted = false")
的注解。但这个解决方案并不完美。具体表现在:
我们还以张三是1801班的教师举例。
加入注解后,我们的确是可以做到可以成功的删除张三了,删除操作后,我们查看数据表,张三的记录的确也还在。但此时,如果我们进行all
或是page
查询,将得到一个500 EntiyNotFound
错误。这是由于在all
查询时,jpa
自动加入了@Where
中的的查询参数,由于关联数据的deleted = true
,进而发生了未找到关联实体的异常。
但事实是:实体虽然被删除,但实际在还在,我们想将其应用到关联查询中。并不希望其发生500 EntiyNotFound
异常。
本文的方案实现了:
- 可以成功的实现软删除。
- 再进行关联删除时,不发生
500 EntiyNotFound
错误。
解决方案
- 即然500是由于注解
@Where(clause = "deleted = false")
引起的,那么我们弃用该注解。 - 我们需要在查询时,加入
deleted = false
的查询。那么我们新建一个接口,并继承jpa
的CrudRepository
,然后重写其查询相关的方法。在重写过程中,加入deleted = false
的查询条件。
实施
初始化
新建KlassTest
, Klass
, Teacher
三个实体,新建BaseEntity
抽象类实体。其中KlassTest
用于演示使用@Where(clause = "deleted = false")
注解时发生的异常。
package com.mengyunzhi.springbootsamplecode.softdelete.entity;import javax.persistence.MappedSuperclass;@MappedSuperclass
public abstract class BaseEntity {private Boolean deleted = false;// setter and getter
}
package com.mengyunzhi.springbootsamplecode.softdelete.entity;import org.hibernate.annotations.SQLDelete;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;/*** 班级*/
@Entity
@SQLDelete(sql = "update `klass` set deleted = 1 where id = ?")
public class Klass extends BaseEntity {@Id@GeneratedValueprivate Long id;private String name;// setter and getter
}
@Entity
@SQLDelete(sql = "update `klass_test` set deleted = 1 where id = ?")
@Where(clause = "deleted = false")
public class KlassTest extends BaseEntity {@Id @GeneratedValueprivate Long id;private String name;
}
重写CrudRepository
package com.mengyunzhi.springbootsamplecode.softdelete.core;import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;import javax.transaction.Transactional;
import java.util.Optional;/*** 应用软删除* 默认的@Where(clause = "deleted = 0")会导致hibernate内部进行关联查询时,发生ObjectNotFound的异常* 在此重新定义接口* 参考:https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa/22202469* @author 河北工业大学 梦云智软件开发团队*/
@NoRepositoryBean
public interface SoftDeleteCrudRepository<T, ID> extends CrudRepository<T, ID> {@Override@Transactional@Query("select e from #{#entityName} e where e.id = ?1 and e.deleted = false")Optional<T> findById(ID id);@Override@Transactionaldefault boolean existsById(ID id) {return findById(id).isPresent();}@Override@Transactional@Query("select e from #{#entityName} e where e.deleted = false")Iterable<T> findAll();@Override@Transactional@Query("select e from #{#entityName} e where e.id in ?1 and e.deleted = false")Iterable<T> findAllById(Iterable<ID> ids);@Override@Transactional@Query("select count(e) from #{#entityName} e where e.deleted = false")long count();
}
新建仓库类
继承spring
的CrudRepository
。
/*** 班级* @author panjie*/
public interface KlassRepository extends SoftDeleteCrudRepository<Klass, Long>{
}
public interface KlassTestRepository extends SoftDeleteCrudRepository<KlassTest, Long> {
}
public interface TeacherRepository extends CrudRepository<Teacher, Long> {
}
测试
package com.mengyunzhi.springbootsamplecode.softdelete.repository;import com.mengyunzhi.springbootsamplecode.softdelete.entity.Klass;
import com.mengyunzhi.springbootsamplecode.softdelete.entity.KlassTest;
import com.mengyunzhi.springbootsamplecode.softdelete.entity.Teacher;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
import org.springframework.test.context.junit4.SpringRunner;import java.util.List;
import java.util.Optional;/*** @author panjie*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TeacherRepositoryTest {private final static Logger logger = LoggerFactory.getLogger(TeacherRepositoryTest.class);@Autowired KlassRepository klassRepository;@Autowired KlassTestRepository klassTestRepository;@Autowired TeacherRepository teacherRepository;@Testpublic void findById() {logger.info("新建一个有Klass和KlassTest的教师");Klass klass = new Klass();klassRepository.save(klass);KlassTest klassTest = new KlassTest();klassTestRepository.save(klassTest);Teacher teacher = new Teacher();teacher.setKlass(klass);teacher.setKlassTest(klassTest);teacherRepository.save(teacher);logger.info("查找教师,断言查找了实体,并且不发生异常");Optional<Teacher> teacherOptional = teacherRepository.findById(teacher.getId());Assertions.assertThat(teacherOptional.get()).isNotNull();logger.info("删除关联的Klass, 再查找教师实体,断言查找到了实体,不发生异常。断言教师实体中,仍然存在已经删除的Klass实体");klassRepository.deleteById(klass.getId());teacherOptional = teacherRepository.findById(teacher.getId());Assertions.assertThat(teacherOptional.get()).isNotNull();Assertions.assertThat(teacherOptional.get().getKlass().getId()).isEqualTo(klass.getId());logger.info("查找教师列表,不发生异常。断言教师实体中,存在已删除的Klass实体记录");List<Teacher> teacherList = (List<Teacher>) teacherRepository.findAll();for (Teacher teacher1 : teacherList) {Assertions.assertThat(teacher1.getKlass().getId()).isEqualTo(klass.getId());}logger.info("删除关联的KlassTest,再查找教师实体, 断言找到了删除的klassTest");klassTestRepository.deleteById(klassTest.getId());teacherOptional = teacherRepository.findById(teacher.getId());Assertions.assertThat(teacherOptional.get()).isNotNull();Assertions.assertThat(teacherOptional.get().getKlassTest().getId()).isEqualTo(klassTest.getId());logger.info("再查找教师列表,断言将发生JpaObjectRetrievalFailureException(EntityNotFound 异常被捕获后,封装抛出)异常");Boolean catchException = false;try {teacherRepository.findAll();} catch (JpaObjectRetrievalFailureException e) {catchException = true;}Assertions.assertThat(catchException).isTrue();}}
总结
使用默认的@SqlDelete
以及@Where
注解时,jpa data
能够很好的处理findById()
方法,但却未能很好的处理findAll()
方法。在此,我们通过重写CrunRepository
的方法,实现了,将进行基本的查询时,使用我们自定义的加入了deleted = true
的方法。而当jpa
进行关联查询时,由于我们未设置@Where
注解,所以将查询出所有的数据,进而避免了当进行findAll()
查询时,有被删除的关联数据时而发生的异常。
spring boot实现软删除相关推荐
- Spring Boot登录选项快速指南
"我喜欢编写身份验证和授权代码." 〜从来没有Java开发人员. 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证. 在本文中 ...
- aws 删除ec2实例_如何在AWS EC2实例上部署Spring Boot应用程序
aws 删除ec2实例 你好朋友, 在本教程中,我们将看到如何在AWS EC2实例上部署Spring Boot应用程序. 这是我们将要执行的步骤. 1.使用Spring Boot Initialise ...
- Spring Boot删除嵌入式Tomcat服务器,启用Jetty服务器
快速指南,在Spring Boot应用程序中排除嵌入式tomcat服务器并添加Jetty Server. 配置删除tomcat并添加Jetty Server. 1.简介 在本教程中,我们将学习如何从S ...
- Spring Boot三合一实验(添加人员,修改人员,删除人员)
目录 基本理论 演示 源码 基本理论 添加和编辑的理论已经在另外一篇博文中体现出来了! Spring Boot修改添加界面二合一 https://blog.csdn.net/qq78442761/ar ...
- spring boot项目自定义数据源,mybatisplus分页、逻辑删除无效解决方法
Spring Boot项目中数据源的配置可以通过两种方式实现: 1.application.yml或者application.properties配置 2.注入DataSource及SqlSessio ...
- fastdfs删除过期文件_Spring Boot 系列:使用 Spring Boot 集成 FastDFS
这篇文章我们介绍如何使用 Spring Boot 将文件上传到分布式文件系统 FastDFS 中. 这个项目会在上一个项目的基础上进行构建. 1.pom 包配置 org.csource fastdfs ...
- 【中软国际实习】Day 11:Spring Boot:TNews项目实现新闻评论
文章目录 新闻评论 实体类 Controller层 Service层 前端界面 小结 新闻评论 实体类 @Entity @Table(name = "t_comment") pub ...
- MyBatis-Plus,搭配 Spring Boot 使用,一篇就够了
以下文章来源方志朋的博客,回复"666"获面试宝典 MyBatis-plus 是一款 Mybatis 增强工具,用于简化开发,提高效率.下文使用缩写 mp来简化表示 MyBatis ...
- Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:任务加油站 原文:toutiao.com/i6861 ...
最新文章
- JVM虚拟机参数配置官方文档
- CENTOS/RHEL 7 系统中设置SYSTEMD SERVICE的ULIMIT资源限制
- java按升序冒泡排序_Java实现冒泡排序算法
- 【数理知识】《数值分析》李庆扬老师-第7章-非线性方程与方程组的数值解法
- 观察者模式--模拟3D彩票公众号
- 获取eclipse 运行时,bundle的相对路径
- jQuery对HTML进行添加元素
- oracle数据库怎么删除数据库,oracle数据库如何删除 oracle数据库删除方法
- boost python导出c++ map_使用Boost生成的Python模块:与C++签名不匹配
- python yield 简单用法_通过实例简单了解python yield使用方法
- dedecms 在模板里引入php文件夹,dedecms如何添加并引入php文件
- Django框架 之 Ajax
- 使用命令行刷新Magento索引管理 Rebuilt Magento Indexes in terminal with php-cli
- C++ 虚函数 纯虚函数 抽象类 代码示例
- 截取字符串_妙用字符串的替换和截取让Shell脚本精准得到你心中的那个“她”...
- 大数据之多数据源综合管理系统:数据源配置管理
- linux 命令 下载 sz,linux - rz/sz 命令上传下载
- SE11字段过多时怎么快速建表
- CGO 之 Dll调用
- android 的一些编译问题
热门文章
- 如何通过c语言获取ipv6邻居表,急求在vc++6.0中获取IPV6地址的方法,高手请进,谢谢!!...
- python3.7.2安装包_Win10下python 2.7与python 3.7双环境安装教程图解
- laravel修改storage目录和bootstrap目录
- 无法启动程序 计算机丢失QT,Win7系统打开程序提示计算机中丢失qt5core.dll如何解决...
- python3.6与3.7有什么区别_Python3.6.6和Python3.7.0的坑
- python相机拍照显示时间_python让图片按照exif信息里的创建时间进行排序的方法...
- 【考前必知】软考考前注意事项
- 备考信息系统项目管理师5点必过经验
- 2021年5月信息系统项目管理师案例分析真题+视频讲解(3)
- 笔记-高项案例题-2018年上-质量管理