我有一个以下用例,我通过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();
}
在这个用例中,我必须为我正在尝试更新的实体尚未创建的事实做好准备,因此我要求创建这样的实体(它的模板与unique属性一样精确) )然后我改变它。 我的dillema如下: 一方面我有两个明显不同的块,我应该在适当的地方使用不同的catch子句但是当我查询时实体不存在的结束情况但是当我尝试创建它时是否有ms(因此ConstraintViolationException) )是不应该经常发生的东西,因为插入因为中间的额外提交/ beginTransaction看起来很腰。
我主要关注会话同步和JDBC连接的额外性能损失,这些都是在提交/开始发生时完成的。
我错了吗?我在找不到优化的地方吗?我错过了什么吗?
提前致谢
答案 0 :(得分:3)
首先,您需要一个交易。没有它,上面的代码将无法工作,因为这可能发生:
问题:在这种情况下数据库是否一致?在类似案件中是否(不)一致?
始终使用交易。数据库针对它进行了优化。如果您遇到问题,请开始考虑您的设计。就像每秒必须处理数千条消息和一样,您的性能工具表明此代码已成为瓶颈。不要相信你的直觉。
答案 1 :(得分:2)
hibernate事务几乎只是数据库事务的包装器。因此,数据库交易会很昂贵。
与往常一样,优化通常最好是拥有清晰安全的代码,而不是试图获得额外的1%性能。但我不知道你的用例。如果上面几秒钟被调用,那么不要担心性能。如果它被称为每秒几百次,那么它可能是一个问题。
如果遇到性能问题,请测量/时间/配置代码,直到找到问题为止。通常,您可以假设问题出现在一个地方,而实际上是在其他地方。
在上述情况下,我会做以下
ConstraintViolationException
阻止日志和continue
,那么为了编写一些额外的逻辑,只需再次尝试,它就会找到另一个事务添加并适当更新的新行。 / LI>
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();
}
}
答案 2 :(得分:1)
我想我实际上已经在我的用例中找到了具体的问题,那就是只在实际需要时打开事务,所以我保存了过早的性能dillema:
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();
}
这将使我能够本地化每个事务中的特定风险,并避免在不需要时提交和打开事务。 感谢您的评论。
答案 3 :(得分:1)
无论你做什么,写操作都不能在事务之外完成,如果没有正在进行的事务并且抛出异常,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.性能方面,它是最好的选择,实际上是一种常见模式,尤其是在使用消息传递和高并发时。