2

由于遗留代码问题,在向数据库插入新行时,我需要手动计算唯一索引并且不能使用 auto_increment。

问题是多个客户端(不同机器)的多次插入可以同时发生。因此,当当前事务处于活动状态时,我需要锁定具有最高 id 的行不被其他事务读取。或者,我可以锁定整个表以防止任何读取。在这种情况下,时间不是问题,因为写入/读取非常罕见(每秒<1 次操作)

它尝试将隔离级别设置为 8 (Serializable),但随后 MySQL 抛出了 DeadLockException。有趣的是,确定下一个 ID 的 SELECT 仍然完成,这与我对可序列化的理解相矛盾。

还将 LockMode 设置为选择的 PESSIMISTIC_READ 似乎没有帮助。

public void insert(T entity) {
    EntityManager em = factory.createEntityManager();
    try {
        EntityTransaction transaction = em.getTransaction();
        try {
            transaction.begin();

            int id = 0;
            TypedQuery<MasterDataComplete> query = em.createQuery(
                    "SELECT m FROM MasterDataComplete m ORDER BY m.id DESC", MasterDataComplete.class);
            query.setMaxResults(1);
            query.setLockMode(LockModeType.PESSIMISTIC_READ);
            List<MasterDataComplete> results = query.getResultList();
            if (!results.isEmpty()) {
                MasterDataComplete singleResult = results.get(0);

                id = singleResult.getId() + 1;
            }

            entity.setId(id);

            em.persist(entity);
            transaction.commit();
        } finally {
            if (transaction.isActive()) {
                transaction.rollback();
            }
        }
    } finally {
        em.close();
    }
}

给应用程序的一些话:
它是 Java-Standalone,在连接到同一个数据库服务器的多个客户端上运行,它应该与多个数据库服务器(Sybase Anywhere、Oracle、Mysql ......)一起工作

目前,我剩下的唯一想法就是进行插入并捕获 ID 已在使用时发生的异常,然后重试。这是有效的,因为我可以假设该列设置为主键/唯一。

4

1 回答 1

0

问题在于,使用PESSIMISTIC_READ时,您会在 ID 最高的行上阻止其他UPDATE 。如果你想阻止其他人的SELECT你需要使用PESSIMISTIC_WRITE

我知道这看起来很奇怪,因为您不打算更新该行.. ..但是如果您在执行 SELECT 时想要其他块,您应该说:“嘿所有.. ..我读了这一行并将更新它”.. .. 这样他们就不会被允许读取该行,因为数据库引擎认为您将在提交之前对其进行修改。

根据文档,SERIALIZABLE 本身将所有普通的 SELECT 语句转换为 SELECT ... LOCK IN SHARE MODE,因此不会超过您已经明确执行的操作。

于 2013-09-14T22:45:24.637 回答