Springboot 优雅的处理数据库报错信息

您所在的位置:网站首页 提取票面返回异常怎么办 Springboot 优雅的处理数据库报错信息

Springboot 优雅的处理数据库报错信息

2024-07-17 10:52| 来源: 网络整理| 查看: 265

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

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异常处理类 @Autowired SqlExceptionHandler sqlExceptionHandler; // sql 报错处理 @ExceptionHandler(value = SQLException.class) @ResponseStatus public 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 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 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/115842821 String 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 value(); }

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

测试实例

这是我的实体类 , 在 ‘cn.enncy.xxx.entity’ 包下,所以把包扫描信息改成 ClassScanner.scan("cn.enncy.xxx.entity"); 在这里插入图片描述 然后把项目跑起来,测试占用信息。

邮箱字段被占用

报错 : Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '[email protected]' for key 'user.user_email_uindex' 在这里插入图片描述 响应 在这里插入图片描述

账号被占用

报错 : Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xxxxx' for key 'user.user_account_uindex' 在这里插入图片描述

原理

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

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


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3