我将尝试在JPA事务隔离级别中描述我的问题 .
数据库结构:
-
Table1
- > PK定义为日期('ddMMyyyy') -
Table2
- > FK到Table1
JPA(隔离级别:: read_commited
) - 代码:
Query query = em.createQuery("from Table1 trd where trd.id = :d");
query.setParameter("d", date);
Table1 t = null;
try{
t = (Table1) query.getSingleResult();
}catch(javax.persistence.NoResultException e){
t = null;
}
if(t==null){
t=new Table1 (date);
em.persist(trd);
}
for(Table2 q:tables2){
q.setTable1(t);
em.merge(q);
}
因此,过程检查db中是否存在记录,如果不存在则创建新记录 . 如果系统只基于一个线程,则方法完全是核心 . 否则可能会出现这样的情况:
-
线程1:检查数据库中是否存在按日期表示的实体
-
线程2:完全一样
他们俩都认为这样的记录还没有存在,所以加上新的记录 . 在提交交易之前一切都还可以 . 第一个将被提交,没有任何例外,购买与主键重复相关的第二个上升异常 .
除了将隔离级别更改为 SERIALIZABLE
之外,是否有可能保留此类情况?
2 回答
是的,一般隔离级别= SERIALIZABLE应该可以解决您的问题 . 将隔离级别更改为最严格的选项时,不要低估副作用 . 它可能会影响数据库利用率以及请求吞吐量 . 也许您可以在创建T1后显式提交TRX,然后打开另一个TRX:EntityManager.getTransaction() . commit()
您仍然必须捕获重复键异常 .
这是在高度并发环境中使用数据库解决的最难的问题之一 . 最后,您唯一真正的解决方案是捕获异常并适当地处理它 .
根据您提供的代码,很难说出可能是什么 . 如果这是一个webapp,很可能你会想要捕获重复的键异常并向最终用户显示一些有用的消息 . 例如“此记录已经创建”,或类似的东西 .