繁体   English   中英

休眠多对多联接表对于继承的实体不持久

[英]Hibernate Many-To-Many join table not persisting for inherited Entity

我已经阅读了有关多对多关系的Hibernate文档 ,并尝试遵循他们的建议,但是我的解决方案并未成功。 希望有人可以阐明这个话题。

我有一个数据库结构,试图通过Hibernate映射多对多关系。 我的想法是,我有许多可能彼此冲突的实体。 在表结构方面,每个实体都是相似的,因此我从一个通用的持久化类AbstractEntity抽象了它们。 由于实体可能会发生冲突,并且可能与任何其他数量的实体发生冲突,因此我定义了一个单独的Conflict休眠实体来定义每个冲突,每个冲突都应通过多对多关系映射到每个冲突该冲突中的实体。

这是实体声明。 我已经将Event作为AbstractEntity Object具体实现的示例。

我的课程和休眠配置:

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEntity {
    protected Set<Conflict> conflicts = null;

    protected AbstractEntity() {
        conflicts = new HashSet<Conflict>();
    }

    @ManyToMany(fetch = FetchType.LAZY,
            targetEntity = Conflict.class)
    @JoinTable(name = "conflict_affected_entity",
            joinColumns = { @JoinColumn(name = "affected_entity_id") }, 
            inverseJoinColumns = { @JoinColumn(name = "conflict_id") })
    public Set<Conflict> getConflicts() {
        Hibernate.initialize(conflicts);
        return conflicts;
    }

    public void setConflicts(Set<Conflict> conflicts) {
        this.conflicts.clear();
        this.conflicts.addAll(conflicts);
    }
}

@Entity
@Table(name = "event")
public class Event extends AbstractEntity {

    private String name;

    public Event() {
        super();
    }

    @Column(name = "name", nullable = false)
    public String getName() {
        return name;
    }

    public void setName(String text) {
        this.name = text;
    }
}

@Entity
@Table(name = "conflict")
public class Conflict {

    private Set<AbstractEntity> affectedEntities = null;

    public Conflict() {
        affectedEntities = new HashSet<AbstractEntity>();
    }

    @ManyToMany(fetch = FetchType.LAZY,
            targetEntity = AbstractEntity.class,
            mappedBy = "conflicts")
    public Set<AbstractEntity> getAffectedEntities() {
        Hibernate.initialize(affectedEntities);
        return affectedEntities;
    }

    public void setAffectedEntities(Set<AbstractEntity> affectedEntities) {
        this.affectedEntities.clear();
        this.affectedEntities.addAll(affectedEntities);
    }
}

在代码中,我需要能够从任一侧创建联接表条目(将AbstractEntity添加到Conflict或将Conflict添加到AbstractEntity)。 下面显示了如何创建示例的示例:

Event event1 = EventDAO.getInstance().get(eventID1);
Event event2 = EventDAO.getInstance().get(eventID2);
Conflict conflict = new Conflict();
conflict.getAffectedEntities().add(event1);
conflict.getAffectedEntities().add(event2);

Hibernate似乎对正在发生的事情有一个线索,所以我觉得我缺少一些简单的东西。 当我创建一个新的Conflict并向其添加一个AbstractEntity时,会创建Conflict和AbstractEntity,但是联接表仍然为空。 谁能为我提供线索以使Hibernate填写空白加入表吗?

您的表有主键吗?

像这样更改您的@ManyToMany映射:

@JoinTable(name = "conflict_affected_entity",
        joinColumns = { @JoinColumn(name = "affected_entity_id", referencedColumnName="primaryKeyOfAffectedEntityColumnName") }, 
        inverseJoinColumns = { @JoinColumn(name = "conflict_id", referencedColumnName="primaryKeyOfConflictColumnName") })

根据所显示的代码(包括如何保存数据),问题出在什么实体拥有该关系。 在您的代码中,您已将@ManyToMany映射到AbstractEntity类中,这意味着AbstractEntity及其子类是拥有该关系的类,并负责联接表的数据库更新。 在您的冲突类中,您已经正确定义了多对多关系,并使用

mappedBy = "conflicts"

您已经告诉Hibernate,多对多关系的第二个定义实际上是指AbstractEntity类中的flicts属性已经映射的多对多关系。 因此,多对多关系的第二个定义不需要执行数据库更新,因为数据库更新由AbstractEntity拥有。

在显示的用于保存数据的代码中,您具有已经持久的Event对象。 然后,您创建一个新的Conflict类,并将关系添加到该类。 问题在于,当您持久化这个新类时,Hibernate将不会持久化该关系,因为多对多定义表明Event对象拥有数据库更新。 因此,要解决此问题,您可以更改映射,以便在Conflict类中声明它,然后AbstractEntity使用“ mappedBy”属性声明对应项,或者可以保留Conflict类,然后使用事件类,并对其进行更新。 就像是:

Event event1 = EventDAO.getInstance().get(eventID1);
Event event2 = EventDAO.getInstance().get(eventID2);
Conflict conflict = new Conflict();
session.save(conflict);
event1.getConflicts().add(conflict);
session.update(event1);
event2.getConflicts().add(conflict);
session.update(event2);

事实证明,该问题不在Hibernate批注中。 相反,问题出在我如何使用带注释的方法访问集合。

正如您在问题中看到的那样,“集合”设置器清除了集合,然后在新集合中添加了所有项目。 更新的代码(有效!)在此处显示:

@ManyToMany(targetEntity = AbstractEntity.class,
        fetch = FetchType.LAZY)
@JoinTable(name = "conflict_affected_entity",
        joinColumns = { @JoinColumn(name = "conflict_id", referencedColumnName = "id") },
        inverseJoinColumns = { @JoinColumn(name = "affected_entity_id", referencedColumnName = "id") })
public Set<AbstractEntity> getAffectedEntities()
{
    Hibernate.initialize(affectedEntities);
    return affectedEntities;
}

public void setAffectedEntities(Set<AbstractEntity> affectedEntities)
{
    this.affectedEntities = affectedEntities;
}

@ManyToMany(targetEntity = Conflict.class,
        fetch = FetchType.LAZY)
@JoinTable(name = "conflict_affected_entity",
        joinColumns = { @JoinColumn(name = "affected_entity_id", referencedColumnName = "id") }, 
        inverseJoinColumns = { @JoinColumn(name = "conflict_id", referencedColumnName = "id") })
public Set<Conflict> getConflicts()
{
    Hibernate.initialize(conflicts);
    return conflicts;
}

public void setConflicts(Set<Conflict> conflicts)
{
    this.conflicts = conflicts;
}

对于将来的观看者来说,这种Hibernate配置(将每一面映射为ManyToMany)会创建两个单向关联:从冲突-> AbstractEntities和从AbstractEntity->冲突。 这意味着,如果您决定使用此配置,则在添加或删除集合中的项目时必须小心,以确保更新连接表条目,以避免违反外键约束。 例如,删除冲突时,我们不能只说ConflictDAO.getInstance.delete(toDelete) 相反,我们必须确保冲突不保留任何关联:

for (AbstractEntity affectedEntity : toDelete.getAffectedEntities()) {
    notifications.add(Notification.forUsersWithAccess(ActionType.UPDATE, affectedEntity));
    // Forcefully remove the associations from the affectedEntity to the Conflict, since we don't want to risk using CascadeType.DELETE
    affectedEntity.getConflicts().remove(toDelete);
}
ConflictDAO.getInstance().delete(toDelete);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM