3

我的 Java 应用程序需要以原子和隔离的方式执行一组 DB 语句。例如,应用程序需要从一个表中读取数据行并更新另一个表中的数据行。

QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner

Object[] params = new Object[] { param };

Connection conn = null;
try {
    conn = ...; // get connection
    conn.setAutoCommit(false);
    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params);

    // logic to get value for update

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id);
    conn.commit();
} catch (SQLException e) {
    //
} finally {
    DBUtils.closeQuietly(conn);
}

事务管理是通过将连接的自动提交设置为 false 并稍后显式提交来实现的,如上所示。但是上面的代码也可以在多线程环境中执行,我还希望两个数据库语句(选择和更新)作为一个整体相互独占运行。

我有一些想法在该方法中使用共享的 Java Lock 对象,如下所示。

在课堂里,

private Lock lock = new ReentrantLock(); // member variable

在方法中,

lock.lock();
try {
    conn = ...; // get connection
    conn.setAutoCommit(false);

    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params);

    // logic to get value for update

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id);
    conn.commit();
} finally {
    DBUtils.closeQuietly(conn);
    lock.unlock();
}

它似乎有能力解决这个问题。但是,我想知道这是否是最佳实践,是否有更好的替代方案(例如框架)?

4

4 回答 4

1

处理此类问题的另一种方法是对所有数据库事务使用可序列化事务隔离级别。这会导致任何一组事务表现得好像它们一次运行一个,而不是实际上使它们一次运行一个。如果您这样做,您应该使用捕获序列化失败 (SQLState 40001) 并重试事务的框架。最大的好处是您无需担心事务之间的特定交互——如果一个事务在它是唯一运行的事物时做正确的事情,那么它将在任何事务组合中做正确的事情。

请注意,所有事务都必须是可序列化的,才能如此简单地工作。

于 2012-04-05T04:12:37.973 回答
1

实现所需原子性的唯一方法是使用数据库中的存储过程来隔离数据并立即将其全部锁定。Java 级别的锁定不能像数据库中的锁定那样容易。

于 2012-04-04T16:38:05.553 回答
1

我的建议是让数据库为您而不是您的应用程序管理这些锁。这可以处理有多个 JVM 运行代码的情况。您提到的锁定机制只能在单个 JVM 中有效。

实现这一点的方法是做一个SELECT ... FOR UPDATE. 这将在选定的行上放置一个锁,并且当您的事务提交或回滚时该锁将被释放。这比表级锁要好,因为这些行仍然可以被其他只想读取当前值但不更新它们的事务读取。如果另一个事务试图获得FOR UPDATE锁,那么它将阻塞,直到第一个事务完成。

于 2012-04-04T16:27:07.953 回答
0

根据我的理解,您是否只想将包含 select 和 update 语句的代码块设为线程安全?这就是 synchronized 关键字的用途。尽管很久以前就问过这个问题,但我只想在这里记下它。将这些代码行放在同步块下。

于 2013-09-13T10:41:03.693 回答