2012-04-03 89 views
0

我正在使用Hibernate 4.1.1很长时间了。 以下内容:我创建了一个由Hibernate的Integrator接口集成的EventListener。到目前为止,这工作!删除EventListener中的实体

我试图删除一个实体,例如一个帐户。 一个帐户与播放列表有一对多关系。

我想要达到的效果: 删除帐户时删除播放列表。

我知道我可以通过在我的Hibernate映射中添加级联行为来实现此目的。 到目前为止,这将工作,但Hibernate是无法提供的功能,以设置级联null值,所以我想要做手工这些步骤:

@Override public boolean onPreDelete(final PreDeleteEvent preDeleteEvent) throws HibernateException 
{ 
    if (preDeleteEvent.getEntity() instanceof PresetEntry) { 
    } else if (preDeleteEvent.getEntity() instanceof AccountEntry) { 
     final Session session = this.sessionFactory.getCurrentSession(); 

     if (!session.getTransaction().isActive()) { 
      session.getTransaction().begin(); 
     } 
     final AccountEntry accountEntry = (AccountEntry) preDeleteEvent.getEntity(); 
     session.createQuery("DELETE FROM PlaylistEntry WHERE ACCOUNT_ID = " + accountEntry.getIdentity()).executeUpdate(); //NON-NLS 
    } else if (preDeleteEvent.getEntity() instanceof PlaylistEntry) { 

    } else if (preDeleteEvent.getEntity() instanceof QueueEntry) { 

    } 
    return false; 
} 

我在以下问题上运行:

  1. 事务已经打开,因为该帐户当前已删除
  2. 调用内部onPreDelete导致的StackOverflowError提交(它无休止地提交删除playlistEntry查询)
  3. 如果查询未提交在onPreDelete方法中,将执行查询,但Swing应用程序开始永久冻结。
  4. 如果不删除外键没有动作异常的播放列表结果

所以,我怎么能解决?

+0

目前,您的代码可以通过'cascade'实现,不是吗? – 2012-04-03 14:45:52

+0

@EugeneRetunsky绝对正确!但是,如果不通过“级联”来实现它,难道不应该实现吗? Hibernate的级联功能缺少太多的SQL级联功能(例如ON DELETE | UPDATE SET NULL) – 2012-04-04 08:05:40

回答

1

提交事务时发生StackOverflow,因为它再次(以及一次又一次)调用onPreDelete处理程序。为了避免你需要检测(在你的监听器中)你已经处理了这个对象的移除。这可以通过下面的方法来完成两种:

private final Collection<Long> processingAccounts = 
     Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>()); 
    ... 
    @Override public boolean onPreDelete(final PreDeleteEvent preDeleteEvent) 
     throws HibernateException { 
     .... 
    } else if (preDeleteEvent.getEntity() instanceof AccountEntry) { 
      // check if it's already been processed 
      if (processingAccounts.contains(preDeleteEvent.getEntity().getId()) 
       return false; 
      // block it by ID 
      processingAccounts.add(preDeleteEvent.getEntity().getId(); 
      try { 

      .... 
      } finally { 
       // release 
       processingAccounts.remove(preDeleteEvent.getEntity().getId() 
      } 
    } 
    return false; 
} 

当然,你可以通过一个复合键或者通过实体锁定(如果有哈希码+等于实现)。但这是如何解决的一般原则。

+0

thx很多,这看起来非常好。但是,我现在正在使用我的休眠会话进行故障排除。方法#1开始一个事务,调用session.delete(AccountEntry)并以提交事务结束。此删除事件触发方法2 - onPreDelete。如果我在这里开始一个事务,则抛出TransactionException:不支持嵌套事务,如果我只是调用session.delete(X);什么都没有发生,如果我在方法#2中进行提交,方法#2的删除事件被执行,但方法#1正在抛出会话关闭!例外。解决方案:打开第二场会议 - 工作。 – 2012-04-05 13:36:48