捕获重复条目异常

Posted

技术标签:

【中文标题】捕获重复条目异常【英文标题】:Catch duplicate entry Exception 【发布时间】:2015-02-19 09:01:09 【问题描述】:

我怎样才能捕捉到这个异常:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: 
                                      Duplicate entry '22-85' for key 'ID_CONTACT'

【问题讨论】:

最好解决导致此异常的问题 这不是问题,但使用 Web 应用程序,用户可以更改 http 请求参数,我想确保一切正常,所以我想添加这一层安全性。 但正如您从堆栈跟踪中看到的那样,在插入期间您违反了约束,最好在将数据发送到数据库之前先验证数据。如果您真的想要,您显然可以捕获异常,但我更喜欢更清洁的解决方案 最干净的解决方案是将用户重定向到一些通用的网络错误页面,该页面不显示异常跟踪(显然)但会提醒系统所有者。比尝试处理这些特殊攻击案例要好得多... 但是你怎么知道它是重复条目异常。异常表示违反约束。如果是另一个约束呢? 【参考方案1】:

我用的是spring,所以我们通过org.springframework.dao.DataIntegrityViolationException解决它

try 
    ao_history_repository.save(new AoHistory(..));
 catch (DataIntegrityViolationException e) 
    System.out.println("history already exist");

但正如@KevinGuancheDarias 提到的那样:

请注意,虽然这有效。 我建议通过以下方式解决问题 在保存之前发出 findBy ,因为这很混乱,我认为这是 不保证它会在未来的版本中工作,甚至可能会破坏 没有通知。

【讨论】:

这在我的项目中对我不起作用我不知道为什么,但是我的 spring 从存储库中抛出了其他异常,请查看我的答案以获取更多信息 对于遇到此答案的任何人,请注意具有多个实例的环境中答案底部的建议。如果我没记错的话,我很可能是这样,如果您的消费服务运行多个实例(例如带有自动缩放组的 AWS EC2),那么您可能会遇到竞争条件并最终导致数据库约束违规抛出。即,findBy 响应返回“未找到”,但另一个实例在 post-findBy 逻辑可以持久化之前持久化了该对象。【参考方案2】:

在我的 Spring 项目中,抛出的异常是 org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: could not execute statement; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement

所以在调试后我有一个父 JpaSystemException 有一个原因 PersistenceException 并且有一个原因 ConstraintViolationException,最后一个有数据库特定的异常,但我最终捕获了 ConstraintViolationException

public static boolean isSqlDuplicatedKey(Exception e) 
        return e.getCause() != null && e.getCause().getCause() != null
                && ConstraintViolationException.class.isInstance(e.getCause().getCause());

// ....

try 
   someRepository.save(some);
 catch (JpaSystemException e) 
    if (ExceptionUtilService.isSqlDuplicatedKey(e)) 
        // Do something to handle it
     else 
        throw e;
    

请注意,虽然这有效。 我建议通过在保存之前发出 findBy 来解决问题,因为这很麻烦,而且我认为不保证它会在未来的版本中正常工作,甚至可能会在没有通知的情况下中断。

【讨论】:

【参考方案3】:

vendorCode 2601 用于unique index constraint 违反,因此您可以通过e.getErrorCode() == 2601 检查 SQLException cewndorCode。示例代码:

try 
    ao_history_repository.save(new AoHistory(..));
 catch (SQLException e) 
    if (e.getErrorCode() == 2601) 
        System.out.println("handle duplicate index error here!");
     else 
        System.out.println("handle other error code here!");
    

【讨论】:

能否提供相关文档链接,方便用户查看其他错误码?【参考方案4】:

看Spring框架source code 查看 spring JDBC 错误解决代码。

org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator#doTranslate

else if (Arrays.binarySearch(this.sqlErrorCodes.getDuplicateKeyCodes(), errorCode) >= 0)

 logTranslation(task, sql, sqlEx, false); return new DuplicateKeyException(buildMessage(task, sql, sqlEx), sqlEx); 

您可以通过多种方式找到不同的异常翻译器:

从您的数据库中弹簧加载元数据/错误代码 - 一位翻译器 Spring 无法连接到 db - 另一个 Hibernate JPA 可能有不同的翻译器

因此,重复行为可能会从 DuplicateKeyException 变为 DataIntegrityViolationException。

【讨论】:

【参考方案5】:

我同意,但我们在我的 Spring 应用程序中有这样的东西:- RequestValidationException/MessageConstants 是自定义的:

import org.springframework.dao.DuplicateKeyException;
|
|
|
catch (Exception exception) 
if(exception instanceof DuplicateKeyException) 
 logger.error("exception as duplicate Name Found: " + exception.getMessage());
 throw new RequestValidationException(MessageConstants.DUPLICATE_NAME_FOUND_ERROR_CD, MessageConstants.DUPLICATE_NAME_FOUND_ERROR_MSG); 
 else
        logger.error("exception on update: " + exception.getMessage());
        throw exception;
    
 

【讨论】:

在我的情况下,异常 instanceof DuplicateKeyException 永远不会进入 if 循环。为什么? @PAA:你能分享堆栈跟踪吗?我的意思是你得到了什么样的异常,你到底在用什么进行数据库事务。【参考方案6】:

A - 详细记录异常

这是我用来记录 SQL 异常的内容,以便我可以确定要捕获什么;

private static void handleSQLError(SQLException e) throws SQLException 
    log.info("SQL Error Type : " + e.getClass().getName());
    log.info("Error Message  : " + e.getMessage());
    log.info("Error Code     : " + e.getErrorCode());
    log.info("SQL State      : " + e.getSQLState());

    throw e;

这是示例输出;

2018 Nis 05 11:20:32,248 INFO MySQLUtil: SQL Error Type : com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException
2018 Nis 05 11:20:32,248 INFO MySQLUtil: Error Message  : Duplicate entry 'test2 CAMT052' for key 'customer_file_customer_file_name_idxu'
2018 Nis 05 11:20:32,249 INFO MySQLUtil: Error Code     : 1062
2018 Nis 05 11:20:32,249 INFO MySQLUtil: SQL State      : 23000
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'test' for key 'customer_file_customer_file_name_idxu'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)

B - 捕获异常并检查参数

要捕获重复键异常,我们需要捕获特定的类,即MySQLIntegrityConstraintViolationException。以下参数也必须匹配;

SQL State      : 23000

所以我使用以下块来捕获重复条目;

try 
    dbUtil.persistEntry(entry);
 catch (SQLException e) 
    if(e instanceof MySQLIntegrityConstraintViolationException) 
        if(e.getSQLState().equals("23000")) 
            if(e.getMessage().contains("Duplicate")) 
                isDuplicateEntryError = true;
            
        
    

【讨论】:

如果有人(如我)来到这里:SQL 状态不是重复输入错误的直接指标。如果您查看 MariaDB 的官方错误文档(它们有共享错误;mariadb.com/kb/en/library/mariadb-error-codes),您会看到导致该状态的多个错误。这里更重要的信息是错误代码,因为它直接映射到错误。 (在某些情况下,您必须检查多个代码,例如我发现死锁可以返回 1213 和 1205)。 TLDR:更好地检查错误代码而不是 sql 状态【参考方案7】:

我使用弹簧。 所以抓住 org.springframework.dao.DuplicateKeyException

try
    ...
 catch (DuplicateKeyException dke) 
    ...
 

【讨论】:

【参考方案8】:

以下代码适用于我:

try 
    requete.executeUpdate();
 catch (final ConstraintViolationException e) 


【讨论】:

【参考方案9】:

捕获 SQLIntegrityConstraintViolationException,如果您使用的是 Java 1.6+

例如

try 
    ps.executeUpdate("INSERT INTO ...");
 catch (SQLIntegrityConstraintViolationException e) 
    // Duplicate entry
 catch (SQLException e) 
    // Other SQL Exception

try 
    ps.executeUpdate("INSERT INTO ...");
 catch (SQLException e) 
    if (e instanceof SQLIntegrityConstraintViolationException) 
        // Duplicate entry
     else 
        // Other SQL Exception
    

【讨论】:

Unreachable catch block for SQLIntegrityConstraintViolationException. This exception is never thrown from the try statement body @Youssef,你的 mysql-connector-java 的版本是多少?您尝试更新的 SQL 是什么? 需要捕获 SQLException。然后您可以检测它是否是 SQLIntegrityConstraintViolationException。请注意,MySQLIntegrityConstraintViolationException 是一种 SQLIntegrityConstraintViolationException。

以上是关于捕获重复条目异常的主要内容,如果未能解决你的问题,请参考以下文章

捕获多个异常-C# [重复]

捕获 iframe 异常 [重复]

捕获不同线程中引起的异常[重复]

在iOS中捕获崩溃和异常[重复]

捕获调用异步方法C#的异常[重复]

在哪里捕获 Callable.call() 引发的异常 [重复]