【问题标题】:Hibernate unable to delete child recordHibernate 无法删除子记录
【发布时间】:2018-10-30 09:50:16
【问题描述】:

在更新(而不是删除)父记录时,我无法删除子记录。另外,我读过其他帖子,但似乎大多数其他帖子都使用注释而不是 xml,因此很难看出它们与我的问题有何关系。

我有两个表:EventInfo 表保存有关事件的信息,然后是 EventLicenseType 表,只有两列,这两列构成主键; EventLicenseType 表中的一列是 EventInfo 表的外键。

问题是我似乎无法删除 EventLicenseType 记录。我尝试了很多不同的东西,但没有什么对我有用。似乎hibernate想将null作为eventinfoId列,这当然是行不通的。我尝试清除 Set 然后进行合并,还专门调用 session.delete(eventlicenseTypeRec) 然后进行合并。两者都不为我工作。

EventInfo.hbm.xml 文件:

<hibernate-mapping default-lazy="true">

<class name="Eventinfo" table="PA_EVENTINFO">
    <id name="eventInfoId" type="int"
        column="PA_EVENTINFOID">
        <generator class="native" />
    </id>
    <property name="eventTypeId" type="java.lang.String"
        column="PA_EVENTTYPEID" length="255" />

        ...Other columns not shown here for brevity...

    <set name="eventLicenceTypeIds" lazy="false" cascade="all-delete-orphan">
        <key column="PA_EVENTINFOID"/>
        <one-to-many class="EventLicenseType" />
    </set>      
</class>

EventLicenseType.hbm.xml 文件:

<hibernate-mapping default-lazy="true">

<class name="EventLicenseType" table="PA_EVENTLICENSETYPE">
    <composite-id>
        <key-property name="licenseTypeId" type="java.lang.Integer" column="PA_LICENSETYPE"/>
        <key-property name="eventInfoId" type="java.lang.Integer" column="PA_EVENTINFOID"/>
    </composite-id>
</class>

这里是 EventInfo 类。同样,实际文件中有更多字段,这只是重要的部分:

public class Eventinfo implements Serializable {
     /** identifier field */
    private int eventInfoId;    

    /** nullable persistent field */
    @Field(name="eventInfo_eventTypeId")
    private String eventTypeId;

    @IndexedEmbedded
    private Set<EventLicenseType> eventLicenceTypeIds;

    /** default constructor */
    public Eventinfo() {}

    public int getEventInfoId() {
        return this.eventInfoId;
    }

    public void setEventInfoId(int eventInfoId) {
        this.eventInfoId = eventInfoId;
    }

    public String getEventTypeId() {
        return this.eventTypeId;
    }

    public void setEventTypeId(String eventTypeId) {
        this.eventTypeId = eventTypeId;
    }

    public Set<EventLicenseType> getEventLicenceTypeIds() {
        return eventLicenceTypeIds;
    }

    public void setEventLicenceTypeIds(Set<EventLicenseType> eventLicenceTypeIds) {
        this.eventLicenceTypeIds = eventLicenceTypeIds;
    }

这里是 EventLicenseType 类

public class EventLicenseType implements Serializable{

    @Field
    private int licenseTypeId;
    private int eventInfoId;

    public int getLicenseTypeId() {
        return licenseTypeId;
    }

    public void setLicenseTypeId(int licenseTypeId) {
        this.licenseTypeId = licenseTypeId;
    }

    public int getEventInfoId() {
        return eventInfoId;
    }

    public void setEventInfoId(int eventInfoId) {
        this.eventInfoId = eventInfoId;
    }
}

这是我在 DAO 中执行的方法。目前只有一条记录与 eventInfo 记录关联,所以我只是想看看是否可以删除该记录。 (还要注意 eventinfo 是在围绕这个的方法中定义的)。

public Eventinfo execute(Session session) throws Exception {

    //Get the existing eventInfo record                 
    Eventinfo existing = (Eventinfo)session.get(Eventinfo.class, eventinfo.getEventInfoId());
    Iterator iter = existing.getEventLicenceTypeIds().iterator();
    if (iter.hasNext()) {
        EventLicenseType license = (EventLicenseType) iter.next();
        iter.remove();
        session.delete(license);
    }

    session.flush();

    return (Eventinfo) session.merge(eventinfo);
}

在上面的 session.flush() 行中,我收到一个错误:java.sql.BatchUpdateException: 无法将值 NULL 插入列 'PA_EVENTINFOID',表 'PA_EVENTLICENSETYPE';列不允许空值。更新失败。它表明hibernate正在尝试做:

update PA_EVENTLICENSETYPE set PA_EVENTINFOID=null where PA_EVENTINFOID=?

为什么不能直接删除记录?为什么要尝试更新? 我也尝试将代码更改为以下代码并得到相同的错误。

public Eventinfo execute(Session session) throws Exception {

    //Clear out the list
    eventinfo.getEventLicenceTypeIds().clear();             

    return (Eventinfo) session.merge(eventinfo);
}

任何人都可以帮助我解决我所缺少的东西,或者为我指明正确的方向吗?

【问题讨论】:

    标签: java hibernate cascade


    【解决方案1】:

    您需要查看两个表之间的映射是单边的还是双边的。基本上,您需要将两个表的行视为对象,并切断 EventInfo 和 EventLicenceType 表的对象之间的所有联系。因此,如果关系仅来自 EventInfo -> EventLicenseType,则需要在 EventInfo 对象中将 EventLicenseType 的值设置为 null。此外,如果有来自 EventLicenseType 的映射,则需要将连接列的值设置为 null。然后 merge() EventInfo 对象。

    无需显式删除或移除 EventLicenseType 对象。如果这里没有对 EventLicenseType 对象的引用,JVM 会将其作为垃圾收集。

    希望能解决您的问题。

    【讨论】:

    • 感谢您的评论。我在帖子中添加了 EventLicenseType 类;我认为这种关系是单方面的。我刚刚尝试清空 eventLicenseTypeIds 并收到错误“拥有实体实例不再引用具有 cascade=all-delete-orphan 的集合”。我用谷歌搜索并了解到在集合上调用 clear() 而不是将其清空是可行的方法,但我已经尝试过了。
    • 您在引用子实体时是否在父实体中设置了@OneToMany(orphanRemoval=true)?
    • 不,因为我没有使用注释。有没有办法在 xml 中做到这一点,它与 cascade="all-delete-orphan" 有什么不同?