【问题标题】:How to lock a MySQL row and release later in application context如何锁定 MySQL 行并稍后在应用程序上下文中释放
【发布时间】:2014-07-30 14:31:36
【问题描述】:

我非常了解SELECT ... FOR UPDATE 方法,但稍后通过同一连接更新时它确实有效。 但是放在应用程序上下文中,情况并不多,因为数据库管理器使用连接池。我想从数据库中获取记录,它们被锁定在下面的控制器中,在没有其他控制器可以读取或更新它们的情况下处理它们,完成工作后更新它们并释放锁定。该怎么做?

我正在使用 Spring JDBCTemplate 并有以下几个类:

ItemDao:

@Service
public class ItemDao extends AppDao {

    // this is supposed to lock the row
    public UserItems getItemsForUpdate(long userId) {
        return jdbcTemplate.queryForObject("select * from tbl_items where ownerId = ?", new UserItemsRowMapper(), userId);
    }

    public void updateItems(UserItems items) {

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(items);
            oos.close();
            byte[] data = baos.toByteArray();

            jdbcTemplate.update("update tbl_items set data = ? where id = ?", new Object[] { data, items.getId() });
        } catch (Exception e) {
            logger.error(e);
        }
    }

}

Web 端点处理程序:

@Controller
public class EnergyFillHandler extends BaseItemsUpdatedHandler<EnergyFillRequest> {

    @RequestMapping(value="/energyFill", method=RequestMethod.POST)
    public @ResponseBody List<BaseResponse> handle(@RequestBody EnergyFillRequest json) {

        UserItems items = itemDao.getItems(u.getId());

        ... do something; if someone else calls getItems 
        he needs to be blocked at this point

        itemDao.updateItems(userItems);

        ... items are now released and anyone can read and obtain lock on them
    }
}

【问题讨论】:

  • 如果我在这里没有遗漏任何明显的东西,请将您的服务设为@Transactional 并设置适当的隔离级别。不知道你的情况,但我认为你可以使用Isolation.SERIALIZABLE
  • 实际上,您需要确保此代码:UserItems items = itemDao.getItems(u.getId()); itemDao.updateItems(userItems); 包含在单个 SERIALIZABLE 事务中。
  • 好的,很好,但是所有这些操作都发生在 EnergyFillHandler 中,它是一个公开的 Web 端点,它无法访问任何事务管理器,因为它们在 DAO 中占有一席之地
  • 但是它可以访问 dao。是什么阻止你让你的 dao 交易?
  • 对不起,我不明白。在 EnergyFillHandler 中,我调用了 DAO 的两个方法——一个获取结果,然后它做一些工作,然后它调用 DAO 的另一个方法来更新 EnergyFillHandler 所做的事情。问题是我应该如何进行事务处理,因为实际工作发生在 DAO 之外。如果您发布一些示例,我将不胜感激

标签: mysql spring transactions jdbctemplate


【解决方案1】:

您的问题似乎是readers-writers problem 的一种风格,它已扩展到数据库中的事务。我将创建一个 SQL 事务类,它主要负责对您的数据库进行 CRUD 操作。然后我会有一个标志,它可以以与互斥锁非常相似的方式被锁定和释放,这样你就可以确保没有两个并发进程在它们的关键部分(执行 UPDATE 或 INSERT 到 DB)在同一时间。

我还相信有一个 readers-writers lock 专门处理控制对共享资源的访问,这可能会引起您的兴趣。

如果您有任何问题,请告诉我!

【讨论】:

    【解决方案2】:

    正如我在 cmets 中提到的,如果您想要/需要在执行某些 Java 代码时在数据库行级别锁定,那么该 Java 代码需要被事务边界包围。例如,如果您通过在服务类中创建新方法来执行此操作,并且在该方法中放置所有需要“锁定”这些行的 Java 代码,或者如果您手动执行此操作,则通过编程事务直接在控制器中代码由你决定。但是,在我看来,您需要拥有这种级别的事务分界才能“锁定”数据库。根据您希望锁定的严格程度,您可以尝试从 SERIALIZABLE 开始,其中行被锁定以进行读写,并且每个操作都以可序列化的方式一个接一个地完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 2012-03-08
      相关资源
      最近更新 更多