在Java中锁定一组DB操作

在Java中锁定一组DB操作,java,locking,transaction-isolation,Java,Locking,Transaction Isolation,在Java应用程序中,我需要以原子和隔离的方式执行一组DB语句。例如,应用程序需要从一个表中读取数据行,并更新另一个表中的数据行 QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner Object[] params = new Object[] { param }; Connection conn = null; try { conn = ...; // get connection conn

在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);
}
在方法上,

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();
}

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

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


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

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

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


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

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

谢谢,Hiro2k。我们的应用程序运行在一个servlet容器中,我相信它将在一个JVM中运行。选择。。。FOR UPDATE在我的情况下可能不起作用,因为我正在更新select语句所操作的表以外的表中的一行。对,但如果您决定添加另一个服务器实例以进行负载平衡或扩展,那么您将遇到此问题。您可以添加选择。。。用于在方法的最开始处更新第二个表中的行,以便其他人必须等到它完成。很好,@Hiro2k。我将在我这方面评估我们的用例,看看是否可以涵盖所有用例。。。对于更新,它有助于防止多个更新同时对同一数据段进行操作。它是否也会阻止当前读取同一数据段?正如我在回答中所说,它只会阻止其他选择。。。获取该数据的更新。通常,您希望允许发生简单的SELECT语句,以实现更好的并发性。想象一下,试图获取当前表上的报告,但必须等待所有行释放其锁。如果您想在使用表时阻止表中的所有读取,那么您需要表级锁,但您的数据库可能有更细粒度的控件。到目前为止,我所说的一切对博士后来说都是正确的。我的建议仅在通过在单个JVM中对同一对象的方法调用更新相关数据时有效。它本身并不锁定数据库级别。这就是说,它不会阻止在其他地方触发的对同一数据块的其他意外更新,例如其他方法调用、其他应用程序。这可以通过使用存储过程来缓解吗?
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();
}