有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java H2数据库升级到1.4.198版后,从select sets outofdate数据更新java H2数据库

我们有一个简单的计数器输入输出项目数据库。到目前为止,我们使用的是H2数据库的1.4.197版。使用此版本执行下面的示例代码段始终意味着计数器为5000。升级到1.4.198或更高版本会导致下面的代码返回不一致的结果,通常在1500到2000之间

public static void main(String[] args) throws SQLException, InterruptedException {
        String url = "jdbc:h2:mem:testdb";

        Connection connection = DriverManager.getConnection(url);
        connection.prepareStatement("CREATE TABLE t1 (id INT PRIMARY KEY, counter INT)").execute();
        connection.prepareStatement("INSERT INTO t1 (id, counter) VALUES (1, 0)").execute();

        int threads = 10;
        int times = 500;

        ExecutorService service = Executors.newFixedThreadPool(threads);
        ExecutorCompletionService<Void> cs = new ExecutorCompletionService<>(service);

        for (int i = 0; i < threads; i++) {
            cs.submit(() -> {
                Connection conn = DriverManager.getConnection(url);
                IntStream.range(0, times).forEach($ -> {
                    try {
                        conn.prepareStatement("UPDATE t1 SET counter = (SELECT counter FROM t1 WHERE id = 1) + 1 WHERE id = 1").executeUpdate();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                });
                return null;
            });
        }

        for (int i = 0; i < threads; i++) {
            cs.take();
        }

        ResultSet resultSet = connection.prepareStatement("SELECT counter FROM t1 WHERE id = 1").executeQuery();
        resultSet.next();
        System.out.println("counter: " + resultSet.getInt("counter"));

    }

我的假设是,SQL下面的select语句在其他事务从表中释放锁之前执行,然后使用select语句中的过期数据执行更新

"UPDATE t1 SET counter = (SELECT counter FROM t1 WHERE id = 1) + 1 WHERE id = 1"

有人有过类似的问题吗


共 (1) 个答案

  1. # 1 楼答案

    正常的SELECT语句本身不会锁定选定的行,并且可以从锁定的行中读取提交的(旧)值。代码中的整个命令周围没有障碍,这就是为什么具有多线程执行的H2的新版本可以同时执行此类命令的原因。多个命令可能会读取相同的旧值并尝试更新该行UPDATE锁定行,但子查询已被计算,并且由于自动提交,在执行命令后立即释放锁。您需要在子查询中使用SELECT … FOR UPDATE

    UPDATE t1 SET counter = (SELECT counter FROM t1 WHERE id = 1 FOR UPDATE) + 1 WHERE id = 1
    

    顺便说一句,H2 1.4.198是一个测试版,有很多已知问题。使用1.4.199或1.4.200代替它更安全