Hibernate多对多连接表不为持久的实体持久化

时间:2014-02-18 20:37:21

标签: java hibernate inheritance hibernate-annotations

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

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

以下是实体声明。我已将Event作为AbstractEntity对象的具体实现的示例。

我的类和休眠配置:

@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填入空白连接表吗?

3 个答案:

答案 0 :(得分:0)

你的桌子有主键吗?

更改@ManyToMany映射:

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

答案 1 :(得分:0)

根据您显示的代码,包括如何保存数据,问题在于实体拥有关系。在您的代码中,您已在AbstractEntity类中映射了@ManyToMany,这意味着AbstractEntity及其子类是拥有该关系的那些,并且负责连接表的数据库更新。在您的Conflict类中,您已正确定义了多对多关系,并使用

mappedBy = "conflicts"

你告诉Hibernate,多对多关系的第二个定义实际上指的是AbstractEntity类中的conflicts属性已经映射的多对多关系。因此,多对多关系的第二个定义不需要执行数据库更新,因为数据库更新由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);

答案 2 :(得分:0)

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

正如您在问题中所看到的,Collection setter清除了集合,然后在新集合中添加任何项目。更新的代码(有效!)如下所示:

@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)创建了两个单向关联:来自冲突 - &gt; AbstractEntities和AbstractEntity - &gt;冲突。这意味着,如果您决定使用此配置,则在添加或删除集合中的项目时必须小心,以确保更新连接表条目以避免外键约束违规。例如,在删除冲突时,我们不能只说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);