![](/img/trans.png)
[英]ConcurrentModificationException upon committing transaction with Hibernate
[英]How expensive is committing a hibernate transaction?
我有以下用例,其中我通过JMS接收到有关实体的消息,该消息是通过实体的唯一属性(不是PK)发出的,它需要我更新实体的状态:
HibernateUtil.beginSession();
HibernateUtil.beginTransaction();
try{
Entity entity = dao.getEntityByUniqueProperty(propertyValue);
if (entity==null){
entity = dao.addEntityByUniqueProperty(propertyValue)
}
entity.setSomeProperty(otherPropertyValue);
HibernateUtil.commitTransaction();
} catch (ConstraintViolationException e){
HibernateUtil.rollbackTransaction();
//Do other things additionally
} catch (StaleStateObjectException e){
HibernateUtil.rollbackTransaction();
//Do other things additionally
} finally {
HibernateUtil.closeSession();
}
在这种用例中,我必须为尚未创建的实体做好准备,因此我要求创建该实体(精确地说,具有唯一属性的模板),然后我改变它。 我的困境如下:一方面,我有两个明显不同的块,并且应该在适当的地方使用不同的catch子句,但最终情况是查询时实体不在那儿,但是当我尝试时又有一个毫秒创建它(因此ConstraintViolationException)是不应该经常发生的事情,因此要插入它是因为中间的一个额外的commit / beginTransaction似乎很累。
我主要关注的是会话同步和JDBC连接在提交/开始发生时带来的额外性能损失。
我错了吗? 我在寻找我不应该去的优化吗? 我想念什么吗?
提前致谢
首先,您需要进行交易。 没有它,上面的代码将无法工作,因为这可能发生:
问题:在这种情况下,数据库是否一致? 在类似情况下是否(不一致)一致?
始终使用事务。 数据库为此进行了优化。 如果您有问题,请开始考虑您的设计。 就像您必须每秒处理数千条消息, 而性能工具表明该代码已成为瓶颈一样。 不要相信您在这里的直觉。
休眠事务几乎只是数据库事务的包装。 因此,这与数据库事务一样昂贵。
通常,与优化一样,通常最好有清晰而安全的代码,而不是试图获得额外1%的性能。 但是我不知道您的用例。 如果以上每秒被调用几次,则不必担心性能。 如果每秒被调用数百次,则可能是一个问题。
如果您遇到性能问题,请测量/时间/配置代码,直到发现问题为止。 通常,您可以假设问题实际上在另一个地方,而这个问题在一个地方。
在上述情况下,我将执行以下操作
ConstraintViolationException
块日志并continue
,而不是编写一些额外的逻辑,而只是再次尝试,它将找到另一个事务添加的新行并进行适当更新。 break
循环 编辑:我将如何做到这一点。
// loop as it is possible we get a retryable exception, see below
while (true){
HibernateUtil.beginSession();
HibernateUtil.beginTransaction();
try{
Entity entity = dao.getEntityByUniqueProperty(propertyValue);
if (entity==null){
entity = dao.addEntityByUniqueProperty(propertyValue)
}
entity.setSomeProperty(otherPropertyValue);
HibernateUtil.commitTransaction();
// success
break;
} catch (ConstraintViolationException e){
HibernateUtil.rollbackTransaction();
// this is ok, another txn must have added the entity at the same time
// try again and it will find the entity this time
continue;
} catch (StaleStateObjectException e){
HibernateUtil.rollbackTransaction();
//Do other things additionally
} finally {
HibernateUtil.closeSession();
}
}
我认为我实际上已经找到了用例的特定问题,那就是仅在实际需要时才打开事务,因此我节省了过早的性能难题:
try {
HibernateUtil.beginSession();
Entity entity = dao.getEntityByUniqueProperty(propertyValue);
if (entity==null){
HibernateUtil.beginTransaction();
try {
entity = dao.addEntityByUniqueProperty(propertyValue)
HibernateUtil.commitTransaction();
} catch (ConstraintViolationException e){
HibernateUtil.rollbackTransaction();
HibernateUtil.closeSession();
HibernateUtil.beginSession();
entity = dao.getEntityByUniqueProperty(propertyValue);
//Do other things additionally
}
}
entity.setSomeProperty(otherPropertyValue);
HibernateUtil.commitTransaction();
} catch (StaleStateObjectException e){
HibernateUtil.rollbackTransaction();
//Do other things additionally
} finally {
HibernateUtil.closeSession();
}
这将使我能够定位每笔交易中的特定风险,并避免在不需要时提交和打开交易。 感谢您的意见。
无论您要做什么,写操作都不能在事务外部完成,如果没有正在进行的事务,Hibernate会抱怨并抛出异常。 所以这里别无选择。
现在,您的用例findOrCreate()
-并非微不足道。 正如您提到的,您可能会遇到比赛条件:
T1: BEGIN TX; T2: BEGIN TX; T1: getEntityByUniqueProperty("foo"); //returns null T1: getEntityByUniqueProperty("foo"); //returns null T1: addEntityByUniqueProperty("foo"); T1: COMMIT; //row inserted T1: addEntityByUniqueProperty("foo"); T2: COMMIT; //constraint violation
所以你必须要么
就个人而言,我会选择选项3。在性能方面,这是最好的选择,实际上这是一种常见的模式,尤其是在处理消息传递和高并发性时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.