在交易开始时锁定表

时间:2013-06-27 09:49:39

标签: java database hibernate locking

由于遗留代码问题,我需要手动计算唯一索引,并且在向数据库中插入新行时不能使用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-Standalone,在连接到同一个数据库服务器的多个客户端上运行,它应该与多个数据库服务器一起使用(Sybase Anywhere,Oracle,Mysql,...)

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

1 个答案:

答案 0 :(得分:0)

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

我知道这似乎很奇怪,因为你不打算更新那一行......但是如果你想在执行SELECT时想要其他的块,你应该说谎:“干草全.. ..我读了这一行并且会更新它“.. ..然后他们将不被允许读取该行数据库引擎认为您将在提交之前修改它。

SERIALIZABLE本身根据文档将所有普通的SELECT语句转换为SELECT ... LOCK IN SHARE MODE,所以不会超过你已经明确做的。