【问题标题】:Spring jdbc 'select for update'Spring jdbc'选择更新'
【发布时间】:2019-10-14 11:47:36
【问题描述】:

我有以下与 Spring JDBC 一起使用的方法

public String getState() {
    String stateLink = template.queryForObject(
            "select state_url from state_scrape_queue where in_use = false ORDER BY scrape_timestamp NULLS FIRST LIMIT 1",
            (result, rowNum) -> {
                return result.getString("state_url");
            });
    return stateLink;
}

我找不到如何使用 Spring JDBC 执行 for update 的示例。我希望 in_use 设置为 true 用于更新。

我需要使用 select 进行更新,因为此应用程序将以多线程方式使用。我不希望多个线程获得同一行,防止这种情况的方法是使用select for update

我可以用纯 JDBC 做到这一点,这是我问如何用纯 JDBC 做到这一点的问题

select "for update" with JDBC?

有人知道这是怎么做的吗?

【问题讨论】:

  • FOR UPDATE 不会更新任何内容,它只会锁定选定的行,就像它已更新一样。因此,您不能使用FOR UPDATE 将某些内容设置为true,您需要执行单独的UPDATE 语句。无论如何,使用 spring-jdbc 的工作方式与直接执行语句相同。就目前而言,尚不清楚您真正要问的是什么。
  • 更新有它的目的,这就是它存在的原因。它在多线程应用程序中非常有用。它会阻止另一个线程获取同一行,因为使用select for update 会锁定该行,并且在此示例中会将in_use 更新为true,因此另一个线程将不会获取同一行。
  • select 中的for update 子句不会更新行,它只会锁定行,因此并发事务无法更新,并且 - 根据锁定模型 - 无法读取该行。您认为它将 in_use 更改为 true 的假设是错误的。
  • @MarkRotteveel 这个给定的答案是使用纯 JDBC 执行select for updatestackoverflow.com/a/46995306/492015我已经用了很长时间了,效果很好。两个线程从来没有得到相同的行。我只需要用 Spring JDBC 做同样的事情
  • 使用 JDBCTemplate 也一样。主要区别可能是您的事务边界。链中是否有任何方法调用该方法@Transactional,如果没有,则表示事务已提交,queryForObject返回时释放的锁。

标签: spring spring-boot jdbc spring-jdbc jdbctemplate


【解决方案1】:

这是我想出的,欢迎提出改进意见

public String getState() throws SQLException {
    String state = null;

    Connection conn = DataSourceUtils.getConnection(template.getDataSource());
    try {
        conn.setAutoCommit(false);

        String[] colNames = { "id", "state_url", "in_use" };
        String query = "select " + Stream.of(colNames).collect(Collectors.joining(", "))
                + " from state_scrape_queue where in_use = false ORDER BY scrape_timestamp NULLS FIRST LIMIT 1 FOR UPDATE";
        System.out.println(query);
        try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
                ResultSet rs = stmt.executeQuery(query)) {
            while (rs.next()) {
                // Get the current values, if you need them.
                state = rs.getString(colNames[1]);

                rs.updateBoolean(colNames[2], true);
                rs.updateRow();
                conn.commit();
            }
        }
    } catch (SQLException e) {
        conn.setAutoCommit(true);
        e.printStackTrace();
    } finally {
        conn.setAutoCommit(true);
    }

    return state;
}

【讨论】:

  • 我在没有关闭 AutoCommit 的情况下尝试了它,它看起来工作正常
猜你喜欢
  • 2018-04-10
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-22
  • 1970-01-01
相关资源
最近更新 更多