如何使用 Spring JDBCTemplate 从唯一的约束冲突中恢复?

Posted

技术标签:

【中文标题】如何使用 Spring JDBCTemplate 从唯一的约束冲突中恢复?【英文标题】:How to recover from a unique constraint violation using Spring JDBCTemplate? 【发布时间】:2015-02-18 15:06:37 【问题描述】:

我正在使用带有 HSQLDB 的 spring jdbcTemplate 将数据插入到 PROFILE 表中,该表有 3 列应用了 UNIQUE 约束,即用户名、电话号码、驾驶执照号码。

我能够使用错误代码识别唯一的约束违规。但由于有多个列具有唯一约束,因此这些信息不足以识别列并显示适当的错误消息。

有什么想法可以实现吗?

【问题讨论】:

如果您阅读异常的消息,它应该会告诉您违反的约束的名称 目的是捕获异常并设置适当的消息响应,例如“此电话号码已注册”、“用户名不可用”等。您的意思是唯一的方法是从例外。还有一个问题。我还需要在我的 java 代码中维护约束名称与列名称的映射,否则当添加额外的唯一约束时,异常处理机制将失败 【参考方案1】:

没有标准的方法可以做到这一点。您可以解析异常消息以查找违反的约束名称。这是一个简单的例子:

public class HSqlDbTest 
    public static void main(String[] args) throws Exception 
        String url = "jdbc:hsqldb:mem:test";
        Class.forName("org.hsqldb.jdbcDriver");
        try (Connection connection = DriverManager.getConnection(url, "sa", "")) 
            String createTableSql = "" +
                    "create table test (" +
                    "  a integer not null,            " +
                    "  b integer not null,            " +
                    "  constraint a_uniq unique (a),  " +
                    "  constraint b_uniq unique (b)   " +
                    ")                                ";
            try (Statement statement = connection.createStatement()) 
                statement.executeUpdate(createTableSql);
            

            try (PreparedStatement statement = connection.prepareStatement("insert into test (a, b) values (?, ?)")) 
                statement.setInt(1, 1);
                statement.setInt(2, 1);
                statement.executeUpdate();

                statement.setInt(1, 2);
                statement.setInt(2, 1);
                statement.executeUpdate();
             catch (SQLIntegrityConstraintViolationException e) 
                Pattern pattern = Pattern.compile("integrity constraint violation: unique constraint or index violation; (\\w+) table: (\\w+)");
                Matcher matcher = pattern.matcher(e.getMessage());
                if (matcher.matches()) 
                    String constraintName = matcher.group(1);
                    String tableName = matcher.group(2);
                    System.out.printf("Violated constrant \"%s\" in the table \"%s\"\n", constraintName, tableName);
                
            
        
    

但我建议你不要走这条路。异常消息不是标准的,如果您更改数据库,可能会更改。如果有多个约束违规,您只会得到其中的第一个。

您应该做的是在您的表格中单独选择以检查该电话号码或用户名是否已经注册。这是完成任务的可靠方式。

【讨论】:

感谢您的解决方案。但我知道最终只有第一次违规才会被处理。 在尝试插入新值之前使用选择的好建议。【参考方案2】:

你可以做的是抓住DuplicateKeyException。然后,您可以从中测试可能导致问题的字段。这样做的好处是您不需要解析供应商实现的异常消息

public void updateWidget(long id,
                         String name,
                         String category) 
    try 
        jt.update("update WIDGET" +
                  "   set DISPLAY_NAME = ?" +
                  "      ,CATEGORY = ?" +
                  " where ID = ?",
                  name,
                  category,
                  id);
     catch (DuplicateKeyException ex) 
        // perform tests in turn, you could also perform a query to return
        // all violation counts if it is more efficient.
        if (jt.queryForObject("select count(1)" +
                              "  from WIDGET" +
                              " where NAME = ?",
                              Integer.class,
                              displayName) > 0) 
            throw new IllegalArgumentException(
                    "Widget name may not be duplicated.");
        
        LOG.warn("Some unique constraint violation occurred: ",
                 ex.getMessage(), ex);
        throw new IllegalArgumentException(
                "Some unique constraint violation occurred.");
    

【讨论】:

以上是关于如何使用 Spring JDBCTemplate 从唯一的约束冲突中恢复?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Spring JdbcTemplate截断表?

如何使用 Spring 的 JdbcTemplate 重用相同的连接?

如何使用 spring 的 jdbcTemplate 在 SQL 查询中指定参数

如何使用 Spring 的 JDBCTemplate 有效地执行 IN() SQL 查询?

如何使用 Spring 的 JdbcTemplate 连接一个简单的 MySql 数据库?

如何执行 IN() 和 WHERE=?使用 Spring 的 JDBCTemplate 进行 SQL 查询