问题描述

对于 oracle 数据库,我们的表通常会创建唯一索引。

不过有时候因为并发等问题,重复插入失败是很正常的,我们希望捕获掉对应的异常,输出 warn 级别的日志即可。

实际使用的 mybatis-plus 作为数据库操作框架,记录一下一些小问题。

单个插入的重复

这个比较符合预期,实现如下:

  [java]
1
2
3
4
5
try { userService.insert(user); } catch (DuplicateKeyException exception) { logger.warn("插入重复,插入失败。", exception); }

直接捕获 DuplicateKeyException,然后处理即可。

批量插入的重复

这里主要是为了记录批量插入的问题。

此处使用的 MP 的 insertBatch 方法,发现主键重复,使用 DuplicateKeyException 却捕获不到对应的异常。

看日志是 BatchExecutorException,发现还是无法捕获。

无奈最后 debug,发现实际抛出的是 MybatisPlusException 异常。

但是这个异常实际上不是很明确,比如数据库非 NULL 的,如果我们插入 NULL,也会报这个错误。

解决方案

把主键冲突的异常获取如下:

  [plaintext]
1
2
org.apache.ibatis.exceptions.PersistenceException: ### Error flushing statements. Cause: org.apache.ibatis.executor.BatchExecutorException: xxx.insert (batch index #1) failed. Cause: java.sql.BatchUpdateException: ORA-00001: 违反唯一约束条件 (xxx)

这是一个本地化的异常提示,我们可以选取唯一的错误码 ORA-00001

  [java]
1
2
3
4
5
6
7
8
9
10
11
try { xxxService.insertBatch(list); } catch (MybatisPlusException exception) { String message = exception.getCause().getLocalizedMessage(); // oracle 的重复异常的唯一码 if(message.contains("ORA-00001:")) { logger.warn("信息插入重复,插入失败。", exception); } else { throw exception; } }

工具方法

当然,可以写成一个工具方法,便于复用。

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** * oracle 重复键异常处理 * * 作用:mybatis insertBatch 会对异常封装,无法只管获取是否为重复。 * @param exception 异常 * @param logInfo 提示 */ public static void oracleDuplicateKeyException(MybatisPlusException exception, String logInfo) { String message = exception.getCause().getLocalizedMessage(); // oracle 的重复异常的唯一码 if(message.contains("ORA-00001:")) { logger.warn(logInfo, exception); } else { throw exception; } }