由于遗留代码问题,我需要手动计算唯一索引,并且在将新行插入数据库时不能使用auto_increment。交易开始时的锁定表
问题是多个客户端(不同机器)的多个插入可以同时发生。因此,当当前事务处于活动状态时,我需要锁定具有最高ID的行被其他事务读取。或者,我可以锁定整个表从任何读取。在这种情况下,时间不是问题,因为写入/读取非常少见(< 1 op /秒)
它试图将隔离级别设置为8(Serializable),但是随后MySQL会引发DeadLockException。有趣的是,确定下一个ID的SELECT仍然完成,这与我对可序列化的理解相矛盾。
同时将LockMode设置为PESSIMISTIC_READ的select,似乎没有帮助。
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的独立,运行在其连接到同一个数据库服务器的多个客户端,它应该与多个DB服务器上运行(任何地方的Sybase,Oracle,MySQL等.. )
目前我剩下的唯一想法就是做插入操作,并捕获ID已经在使用时发生的异常,然后重试。这可以工作,因为我可以假定该列设置为主键/唯一。
你有选择通过它实现一个服务并暴露数据库吗? –
不可悲。整个架构是一片混乱(最多15年),所有客户端都直接连接到数据库。 当我现在要实现它时,我会选择一个连接到数据库和瘦客户端的中央服务器,它只需要从服务器请求并显示数据。但不幸的是,客户有超过20万的LOC,并且完全重写它不是一种选择。 :( – ssindelar
每次生成一个唯一标识符怎么样?由基于时间的部分加上随机部分组成,或者加密安全的唯一标识符。没有事务隔离或乐观锁定问题,并且易于实现。 – Grzegorz