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

标签: java mysql hibernate jpa sqlexception


【解决方案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。
          猜你喜欢
          • 1970-01-01
          • 2012-07-13
          • 2011-12-07
          • 2018-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多