目录

  • 完整性约束报错处理
  • 测试实例
    • 邮箱字段被占用
    • 账号被占用
  • 原理

Duplicate entry 'xxx' for key 'xxxx'

平时我们遇到 mysql 报错的时候很难处理,例如字段重复了,破坏了完整性约束,但是我们又不可能每个唯一字段都去进行查询,看看是不是重复了,这样相当于写死了代码。

那么如何动态的去知道那个信息被占用,或者重复了呢? 这里就需要用到反射的原理


首先我们得学会捕获全局异常 :

使用 @RestControllerAdvice 环绕增强 ,然后再使用 @ExceptionHandler 注解,在某个方法上面给指定的异常类进行注解, Speingboot 会自动将异常分发给你的方法进行处理。

这里的 @ResponseHandler 注解是统一信息处理注解,可以在我的文章 https://blog.csdn.net/qq_31254489/article/details/119772338 中去学习配置,如果你不想用,可以删掉。

@RestController
@ResponseHandler
@RestControllerAdvice
@Api(tags = "統一错误请求控制")
public class MyErrorController implements ErrorController {// 统一sql异常处理类@AutowiredSqlExceptionHandler sqlExceptionHandler;// sql 报错处理@ExceptionHandler(value = SQLException.class)@ResponseStatuspublic String sqlError(SQLException e) {// 我们交给异常处理类去处理return sqlExceptionHandler.handle(e);}
}

具体流程如下

完整性约束报错处理

一般就是插入的时候和已经有的数据重复了

接下来复制粘贴下面2个类,并要遵守以下规则

  • 数据库的约束名称必须遵守 表名.表名_键1_键2_键3_xxx_uindex 例如 user.user_name_unidex 意思就是 user 表下的 name 的唯一约束。
  • 实体类的名称,还有属性,必须和数据库中的一致,mysql 必须遵守下划线命名规则, java 则可以使用 驼峰或者下划线。
  • 使用 @HandleSqlException 注解去方法上面标注需要处理的具体异常。 例如 下面的 integrityConstraint 方法。
/*** sql 错误异常处理** @author enncy*/
@Component
public class SqlExceptionHandler {/***  分发异常处理* @return: void*/public String handle(SQLException e) {// 获取当前类的方法Optional<Method> first = Arrays.stream(SqlExceptionHandler.class.getDeclaredMethods())// 寻找有 HandleSqlException 注解的方法.filter(m -> m.isAnnotationPresent(HandleSqlException.class))// 寻找和参数 e 的类相等的 HandleSqlException .filter(m -> m.getAnnotation(HandleSqlException.class).value() == e.getClass()).findFirst();// 如果存在,则分发给指定的方法if( first.isPresent()){try {return (String) first.get().invoke(this, e);} catch (IllegalAccessException | InvocationTargetException exception) {return "服务器内部出现异常";}}else{return "服务器内部出现异常";}}/***  完整性约束被破坏处理* @return: java.lang.String*/@HandleSqlException(SQLIntegrityConstraintViolationException.class)public String integrityConstraint(SQLException e) {// 匹配数据库报错信息String regex = "Duplicate entry '(.*?)' for key '(.*?)\\..*?_(.*?)_uindex'";Matcher matcher = Pattern.compile(regex).matcher(e.getMessage());// 如果存在信息if (matcher.find()) {// 获取被占用的值String value = matcher.group(1);// 获取数据库表String table = matcher.group(2);// 获取发生冲突的约束键String keys = matcher.group(3);// 扫描实体类List<Class<?>> scan = ClassScanner.scan("cn.enncy.funny.entity");// 寻找冲突的字段的注解描述String description = scan.stream().filter(BaseEntity.class::isAssignableFrom).filter(c -> c.getName().toLowerCase().contains(table)).map(c -> {// 获取实体类的属性值Field[] declaredFields = c.getDeclaredFields();for (Field declaredField : declaredFields) {// 驼峰转下划线,这里的 humpToUnderline 方法参考文章 : https://blog.csdn.net/qq_31254489/article/details/115842821String name = StringUtils.humpToUnderline(declaredField.getName());// 如果属性包含在 keys 的值里面if (keys.contains(name)) {// 返回属性上面的 注解信息return declaredField.getAnnotation(ApiModelProperty.class).value();}}return "";}).findFirst().orElse("");return description + " " + value + " 已经被占用";}return "信息已经被占用";}
}
/*** @author enncy*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandleSqlException {Class<? extends SQLException> value();
}

然后修改完整性约束处理的里面的包路径 cn.enncy.funny.entity , 这个需要你改到自己项目的实体类包下,也就是POJO类所在的包。


测试实例

这是我的实体类 , 在 ‘cn.enncy.xxx.entity’ 包下,所以把包扫描信息改成 ClassScanner.scan("cn.enncy.xxx.entity");

然后把项目跑起来,测试占用信息。

邮箱字段被占用

报错 : Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xxxxxxxxxx@qq.com' for key 'user.user_email_uindex'

响应

账号被占用

报错 : Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xxxxx' for key 'user.user_account_uindex'


原理

大概的就是利用 mysql 报错的信息, 然后进行字符串处理。
思路 :

  • 全局获取 mysql 的报错信息
  • 对报错信息进行正则表达式判断,取出有用的信息。
  • 利用反射原理找出和报错信息所对应的实体类
  • 反射找出实体类的字段和注释信息
  • 最后返回统一信息

Springboot 优雅的处理数据库报错信息相关推荐

  1. SpringBoot 集成 druid 监控数据库报错 Failed to bind properties under ‘xxxx‘ to javax.sql.DataSource 解决(含配置源码)

    What Druid是一个JDBC组件,它包括三部分: • DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系. • DruidDataSource 高效可管 ...

  2. 关于springboot项目连接oracle数据库报错 ORA01017的改正

    拉取的项目本身用的是mysql数据库,改用orcl数据库,引入 驱动,连接时报ORA01017 ,确认用户名和密码是正确的.查看报错,之前有druid驱动,注释了就不报错了.

  3. Springboot连接mysql数据库报错

    Springboot连接mysql数据库报错java.sql.SQLException: Access denied for user ''@'localhost' (using password: ...

  4. Emoji表情符号录入MySQL数据库报错的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> 前言:手机app应用评论的时候,恢复表情符号,提示失败. 1,查看tomcat后台日志,核心报错信息如下:   Caused ...

  5. mysql数据库报错1146_关于MySQL报错:[ERR] 1146

    最近因为电脑重装了系统,导致自己原本的数据库呗覆盖,需要重新重新安装数据库,但是由于我之前数据库版本是mysql 5.0.22,版本太低,所以小编决定安装mysql 5.7.23版本的,一开始没什么问 ...

  6. 报错信息为:Failed to configure a DataSource: ‘url‘ attribute is not specified and no embedded datasource

    报错信息为:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource ...

  7. Laravel 队列:如何查看队列报错信息

    Laravel 队列在执行时,如黑盒一样,出错时我们该如何调试呢? 方法一.sync 驱动 如果你在开发环境,修改 .env: QUEUE_DRIVER=sync 这样就可以直接通过 Web 请求触发 ...

  8. 解决高版本SpringBoot整合swagger时启动报错:Failed to start bean ‘documentationPluginsBootstrapper‘ 问题

    一.控制台的报错信息 2021-12-29 15:15:04 [main] ERROR org.springframework.boot.SpringApplication - Application ...

  9. eclipse spring mysql,eclipse环境下的springboot框架+mybatis访问MySQL报错空指针

    "/")public classTestController { @RequestMapping("/login")publicString login() { ...

最新文章

  1. 国产Linux发行版再添一员,操作界面不输苹果!
  2. ruby安装插件报错
  3. python re正则匹配_python re正则表达式模块
  4. ECMAScript 对象类型
  5. excel趋势线公式导出_Java 添加、读取、删除Excel中的图表趋势线
  6. [HNOI2016] 大数(莫队)
  7. 卡巴斯基亚太区总经理:不做免费杀毒厂商
  8. mysql文件导出NULL值处理_Mysql select into outfile NULL值导出的处理方法
  9. LCS 最大子段和,最大子段和在原数组的首末地址
  10. CodeProject - 在C#使用SHGetFileInfo获取(管理)文件或者文件夹图标(C#封装Win32函数的一个例子)...
  11. 20190925:(经典算法系列)河内之塔
  12. CentOS7添加阿里云yum源
  13. xml简单理解,xml增删改操作,仅作笔记,不作为学习借鉴
  14. linux监控程序-程序自动重启方法(转)
  15. 完美解决Win11无法启动安全中心
  16. 第一章 网络入门【仅参考】
  17. 7-53 奥运排行榜 (25 分)
  18. SQL Server新增Contained Database功能
  19. 史上最全亚马逊申诉模板!!!!
  20. VMware P2V---从物理机到虚拟机(二)

热门文章

  1. canvas牛奶液体底部动画js特效
  2. 【数据挖掘】分类与回归预测
  3. 深度学习——评估学习模型
  4. 维护国家主权,保卫国家安全|科力锐助力中华人民共和国无锡出入境边防检查站灾备建设
  5. java getmethod_java中getMethod*()和getDeclaredMethod*()的区别
  6. 机械革命极光Pro开机错误重启无法进入桌面怎么办?
  7. Docker学习:容器五种(3+2)网络模式 | bridge模式 | host模式 | none模式 | container 模式 | 自定义网络模式详解
  8. 史上最全的常用中英文学术网站(三)
  9. 基于PBOC电子钱包的圈存过程详解
  10. 华为怎么分屏操作技巧_糖豆人终极淘汰赛扒拉怎么操作 扒拉抓人技巧介绍