Hibernate级联删除无法正常工作

时间:2009-09-17 12:50:23

标签: java database hibernate

我在Hibernate中尝试删除时遇到了问题。每当我尝试删除时,由于存在子记录而导致问题,因此无法删除父级。我想删除孩子和父母。这是我的父映射:

<set name="communicationCountries" inverse="true" cascade="all,delete-orphan">
       <key column="COM_ID" not-null="true" on-delete="cascade" />
       <one-to-many class="com.fmr.fc.portlet.communications.vo.CommunicationCountry"/>
</set>

以下是子类的映射:

<many-to-one name="communication" column="COM_ID" not-null="true" class="com.fmr.fc.portlet.communications.vo.Communication" cascade="all"/>

编辑 - 当我执行插入操作时,数据将插入父项和子项中。

当我使用具有我想要修改的对象ID的新对象进行更新时,父级会更新,但是第二次添加任何现有子级。我似乎无法移除孩子。当我使用ID检索对象并修改它时,我收到一个错误告诉我org.hibernate.LazyInitializationException:无法初始化代理 - 拥有的会话已关闭。我怀疑这是因为在一个getHibernateTemplate()调用中我得到了对象,我将它保存在另一个中,这是两个不同的会话?

当我删除时,由于存在子项,我收到错误。我知道我只是做了一些非常愚蠢的事情,因为我不知道这一切是如何运作的。

以下是我的更新和删除方法,在这种情况下,更新/保存是在保存之前检索和修改。删除使用的新对象ID与我要删除的DB中的ID相同:

public void deleteCommunication(Communication comm) throws DataAccessException 
{
    getHibernateTemplate().delete(comm);
}

public void saveCommunication(Communication comm) throws DataAccessException
{       
    Communication existing = (Communication)getHibernateTemplate().load(Communication.class, comm.getComId());
    existing.getCommunicationCountries().clear();

    getHibernateTemplate().saveOrUpdate(existing);      
}

更新 所以这是我的新方法,但仍然没有快乐。我认为我的问题与孩子没有被加载/初始化等有关。虽然删除了,但我不明白为什么没有发生级联删除。

非常感谢你们的帮助。我已经完成了这项工作的截止日期,如果我周末没有解决这个问题,我将不得不求助于执行HQL查询,因为我知道这对我有用:(

public void deleteCommunication(Integer id) throws DataAccessException 
    { 
        HibernateTemplate hibernate = getHibernateTemplate();
        Communication existing = (Communication)hibernate.get(Communication.class, id);
        hibernate.initialize( existing.getCommunicationCountries());
        hibernate.delete(existing);
    }

    public void updateCommunication(Communication comm) throws DataAccessException
    {       
        HibernateTemplate hibernate = getHibernateTemplate();
        Communication existing = (Communication)hibernate.get(Communication.class, comm.getComId());
        hibernate.initialize( existing.getCommunicationCountries());
        existing.getCommunicationCountries().clear();
        hibernate.saveOrUpdate(existing);       
    }

1 个答案:

答案 0 :(得分:2)

没有特别的顺序:

A)假设代码中的“myID”是您实体的标识符,您应该使用session.get()代替条件 - 它更快,更明确更容易:

MyObject obj = (MyObject) session.get(MyObject.class, new Long(1));

B)如果您正在使用Spring(通过getHibernateTemplate()调用判断),您应该始终如一地使用它:-)并且除非绝对必要,否则不要直接调用会话 - 而且它很漂亮从来没有必要。因此,上述get方法将成为:

MyObject obj = (MyObject) getHibernateTemplate().get(MyObject.class, new Long(1));

如果您需要编写基于条件的查询,可以使用DetachedCriteriaHibernateTemplate.getByCriteria()方法:

DetachedCriteria crit = DetachedCriteria.forClass(MyObject.class)
 .add(Property.forName("myId").eq( new Long(1) ) );
List results = getHibernateTemplate().findByCriteria(crit);

C)您通常不应该evict()来自会话的对象(无论如何在关闭之前立即执行此操作)。您通常也不应close()从HibernateTemplate获得的会话。

D)最后,就自动保存子项(一对多集合元素)而言 - 请查看this example,它可以很好地解释不同的级联设置。如果您仍然遇到问题,请发布您的映射/代码。

更新(根据问题解释):

1)除了子类(<many-to-one name="communication" cascade="all"/>)中父级的级联外,您的映射看起来没问题。你很可能不想要这个。

2)抛出LazyInitializationException因为Hibernate默认将集合映射为lazy,这意味着在第一次访问之前不会加载子(communicationCountries)。如果在会话已关闭时发生该访问,则抛出异常。您可以通过在集合上调用Hibernate.initialize()来确保填充集合。

3)你的delete()应该可以正常工作,因为你在Hibernate返回的实体实例上调用它而不是你自己创建的实例(比如,从远程调用解组),其中“communicationCountries”集合不是填充。为了让Hibernate删除子项,它需要知道它们存在。

4)另一方面,你的更新()是错误的。你正在加载一个实体,清理它的子节点并再次保存它 - 这本身很好 - 但它与传入的参数没有关系。