2012-06-21 101 views
0

在webservice + hibernate中发生死锁......不知道为什么。休眠死锁

我有以下的Web服务代码(函数的核心代码)

public String createImageRecipe(...) 
{ 
    Session session = ICDBHibernateUtil.getSessionFactory().getCurrentSession(); 
    try 
    { 
     session.beginTransaction(); 

     User user = (User) session.load(User.class, userid); 

     // Create the recipe 
     Recipe recipe = new Recipe(); 
     ... 
     ... 
     ... 

     session.save(recipe); 
     session.save(user); 

     ... 
     ... 
     ... 

     session.update(recipe); 

     // Add new entry to activity log 
     Activitylog activityLog = new Activitylog( user, 
                (byte) ActivityTypeEnum.USER_SHARED_RECIPE.ordinal(), 
                new Date()); 
     activityLog.setRecipe(recipe); 
     TimelineProcessor.saveTimeline(activityLog, null, true); 

     session.getTransaction().commit(); 

     endMeasurement(); 

     return recipe.getRecipeid() + "," + recipe.getImageRecipeUrl(); 
    } 
    catch (RuntimeException e) 
    { 
     ICDBHibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback(); 
     throw e; 
    } 
} 

现在的时间表线程(TimelineProcessor)进行以下(得到消息后):

Session session = ICDBHibernateUtil.getTimelineSessionFactory().openSession(); 
    try { 
     session.getTransaction().begin(); 

     ... 
     ... 
     ... 

     TimelineId tlId = new TimelineId(); 
     tlId.setUserId(userId); 

     User u = new User(); 
     u.setUserid(userId); 

     Timeline tl = new Timeline(tlId, timelineData.getActivitylog(), u); 
     session.save(tl); 

     session.getTransaction().commit(); 
     session.close(); 
    } catch (Exception e) { 
     logger.error("Error while processing timeline :: ", e); 
     session.getTransaction().rollback(); 
    } 
} 

从异常日志: 引起:com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁定时发现的死锁;尝试sun.reflect.NativeConstructorAccessorImpl.newInstance0(本机方法)

org.hibernate.HibernateException重启交易 :非法尝试用两次公开会议 集合在org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection关联。的java:435)

ERROR com.icdb.TimelineProcessor - processTimeline - 错误而处理的时间表:: org.hibernate.exception.LockAcquisitionException:无法插入:[com.icdb.data.Activitylog]

引起通过:com.mysql.jdbc.exceptions.jd bc4.MySQLTransactionRollbackException:尝试获取锁定时发现的死锁;尝试sun.reflect.NativeConstructorAccessorImpl.newInstance0(本机方法)

ERROR com.icdb.TimelineProcessor重启交易 - processTimeline - 错误在处理时间表:: org.hibernate.HibernateException:非法尝试集合有两个关联打开的会话

真的会帮助并欣赏... 非常感谢

回答

2

那么Hibernate是为了告诉你什么不喜欢:

Illegal attempt to associate a collection with two open sessions 

您正在打开两个会话,并且每个会话都有一个事务,并且当您在第二个会话中保存新时间轴时,它具有一个activityLog,并且该ActivityLog具有一个用户和一个源于第一个会话的配方。这就是Hibernate咆哮的地方。

我不知道我看到你正在努力完成什么。是否有理由需要两个单独的会话?时间线是与配方/用户实体在语义上相关联的东西,还是更像时间戳集合?在前一种情况下,您可能希望将用户,配方和时间线存储在同一个集合中,否则如果一个事务成功并且另一个失败,则最终会导致数据损坏。在后一种情况下,Timestamp服务应该将值对象作为参数而不是实体,因为您可能想要从应用程序的不同部分访问它。

+0

我明白了。那么时间线是用来记录所有的用户活动(以时间线的形式显示,就像在facebook中一样)。我在一个线程中使用它,因为我需要服务调用的速度很快,而且这可以在BG中完成。所以我从你的回答中了解到 - 我应该通过Ids和创建用户u =新用户(); u.setId(i_userId),所以如果理解正确,那么它应该解决这个问题? – user1136875

+2

这就是我会这样做的,特别是因为你有一个SessionFactory和一个TimelineSessionFactory,这是一个提示,有一天时间轴可能最终存储在一个完全不同的数据库服务器上,然后你需要传递消息而不是实体。如何实现它是另一个问题:您可以让TimelineService只接收原始数据,如int和Strings,然后应用程序的其他部分也可以访问它,而无需知道User类。或者您可以使用DTO副本,或者自己制作,或者使用推土机等工具。两种方式都有优点。 – wallenborn

+0

非常感谢。是一个很大的帮助。明白了你的观点 – user1136875