繁体   English   中英

事务开始时锁定表

[英]Lock Table at the begin of a Transaction

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

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

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

还将LockMode设置为select的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独立的,在连接到同一数据库服务器的多个客户端上运行,并且应与多个数据库服务器(Sybase Anywhere,Oracle,Mysql等)一起使用。

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

问题在于,使用PESSIMISTIC_READ会阻止具有最高ID的行上的其他UPDATE 如果要阻止其他人的SELECT ,则需要使用PESSIMISTIC_WRITE

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM