由于遗留代码问题,在向数据库插入新行时,我需要手动计算唯一索引并且不能使用 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 已在使用时发生的异常,然后重试。这是有效的,因为我可以假设该列设置为主键/唯一。