Hibernate多对多关联表在继承实体中无法持久化

4
我已阅读关于多对多关系的 Hibernate文档 ,并尝试按照他们的建议进行操作,但我的解决方案并不成功。希望有人能够为这个问题提供一些指导。
我有一个数据库结构,我正在尝试通过Hibernate映射多对多关系。这个想法是我有许多可能会互相产生冲突的实体。在表结构上,每个实体都类似,因此我从一个公共持久化类AbstractEntity中抽象出它们。由于实体之间可能存在冲突,并且可以与任意数量的其他实体发生冲突,因此我定义了一个单独的Conflict Hibernate实体来定义每个冲突,这应该通过多对多关系映射到在该冲突中的每个实体。
这里是实体声明。我已经包括Event作为AbstractEntity对象的具体实现的示例。
我的类和Hibernate配置:
@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似乎知道发生了什么,所以我觉得我可能错过了一些简单的东西。当我创建一个新的“冲突”并将“AbstractEntity”添加到其中时,会创建Conflict和AbstractEntity,但连接表保持为空。有人能给我提供线索,告诉我需要做什么才能让Hibernate填充空白的连接表吗?

你的级联设置是什么,你在哪些实体上调用了保存操作?根据上面展示的代码,当把抽象实体添加到冲突实体中时,它们被持久化了,这让我感到惊讶,因为似乎在关系上没有设置级联? - Hedley
@Hedley - 看起来 Hibernate 文档指定了 PERSISTMERGE 默认已经配置好了。我也尝试了指定 ALL,但是还是没有运气。我编辑了问题,包括我正在使用的提交新 Conflict 的代码。 - shawmanz32na
3个回答

1
根据您展示的代码,包括数据保存方式,问题出在哪个实体拥有关系。在您的代码中,您已经在 AbstractEntity 类中映射了 @ManyToMany,这意味着 AbstractEntity 及其子类是拥有关系并负责加入表的数据库更新。在 Conflict 类中,您已经正确定义了多对多关系,并且通过使用...
mappedBy = "conflicts"

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

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

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);

0
你的表有主键吗?
将你的@ManyToMany映射更改为以下内容:
@JoinTable(name = "conflict_affected_entity",
        joinColumns = { @JoinColumn(name = "affected_entity_id", referencedColumnName="primaryKeyOfAffectedEntityColumnName") }, 
        inverseJoinColumns = { @JoinColumn(name = "conflict_id", referencedColumnName="primaryKeyOfConflictColumnName") })

我按照你的建议更改了AbstractEntity上的@ManyToMany映射。我在两个表上都有一些真正无聊的id字段作为主键,我在映射中使用了它们,但是Hibernate的行为没有改变。尝试的映射如下:`@JoinTable(name = "conflict_affected_entity", joinColumns = { @JoinColumn(name = "affected_entity_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "conflict_id", referencedColumnName = "id") })' - shawmanz32na

0
事实证明问题并不在于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)创建了两个单向关联:从Conflict -> AbstractEntities和从AbstractEntity -> Conflicts。这意味着如果您决定使用此配置,则在添加或删除集合中的项目时必须小心,以确保加入表条目得到更新,以避免外键约束违规。例如,当删除Conflict时,我们不能只说ConflictDAO.getInstance.delete(toDelete)。相反,我们必须确保Conflict不保留任何关联:

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接