简体   繁体   English

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

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

I have read the Hibernate Documentation regarding many-to-many relationships, and have attempted to follow their suggestions, but my solution has not been successful. 我已经阅读了有关多对多关系的Hibernate文档 ,并尝试遵循他们的建议,但是我的解决方案并未成功。 Hopefully someone can shed some light on the topic. 希望有人可以阐明这个话题。

I have a database structure in which I am trying to map a many-to-many relationship via Hibernate. 我有一个数据库结构,试图通过Hibernate映射多对多关系。 The idea is that I have a number of Entities which may be in conflict with each other. 我的想法是,我有许多可能彼此冲突的实体。 Table-structure-wise, each of these Entities are similar, so I've abstracted them from a common persisted class, AbstractEntity . 在表结构方面,每个实体都是相似的,因此我从一个通用的持久化类AbstractEntity抽象了它们。 Since the Entities can be in conflict, and can be in conflict with any number of other Entities, I've defined a separate Conflict Hibernate Entity to define each of the Conflicts, which should get mapped via a many-to-many relationship to each of the Entities that are in that Conflict. 由于实体可能会发生冲突,并且可能与任何其他数量的实体发生冲突,因此我定义了一个单独的Conflict休眠实体来定义每个冲突,每个冲突都应通过多对多关系映射到每个冲突该冲突中的实体。

Here are the Entity declarations. 这是实体声明。 I have included Event as an example of a Concrete implementation of the AbstractEntity Object. 我已经将Event作为AbstractEntity Object具体实现的示例。

My Classes and Hibernate Configuration: 我的课程和休眠配置:

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

In the code, I need to be able to create the joining table entries from either side (either adding an AbstractEntity to a Conflict or adding a Conflict to an AbstractEntity). 在代码中,我需要能够从任一侧创建联接表条目(将AbstractEntity添加到Conflict或将Conflict添加到AbstractEntity)。 An example of how I'm creating an example is shown here: 下面显示了如何创建示例的示例:

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 seems to have a clue as to what's going on, so I feel like I'm missing something simple. Hibernate似乎对正在发生的事情有一个线索,所以我觉得我缺少一些简单的东西。 When I create a new Conflict , and add an AbstractEntity to it, the Conflict and AbstractEntity are created, but the joining table remains empty. 当我创建一个新的Conflict并向其添加一个AbstractEntity时,会创建Conflict和AbstractEntity,但是联接表仍然为空。 Can anyone provide me a clue to what I need to do to make Hibernate fill in the blank joining table? 谁能为我提供线索以使Hibernate填写空白加入表吗?

Do your tables have primary keys? 您的表有主键吗?

Change your @ManyToMany mapping as such: 像这样更改您的@ManyToMany映射:

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

Based on the code you have shown, including how you are saving the data, the problem is with what entity owns the relationship. 根据所显示的代码(包括如何保存数据),问题出在什么实体拥有该关系。 In your code, you have mapped the @ManyToMany in your AbstractEntity class, which means that the AbstractEntity and its subclasses are the ones that own the relationship, and are responsible for the database updates for the join table. 在您的代码中,您已将@ManyToMany映射到AbstractEntity类中,这意味着AbstractEntity及其子类是拥有该关系的类,并负责联接表的数据库更新。 In your Conflict class, you have correctly defined the many to many relationship, and by using the 在您的冲突类中,您已经正确定义了多对多关系,并使用

mappedBy = "conflicts"

you have told Hibernate that this second definition of the many to many relationship actually refers to the many to many relationship that is already mapped by the conflicts attribute in the AbstractEntity class. 您已经告诉Hibernate,多对多关系的第二个定义实际上是指AbstractEntity类中的flicts属性已经映射的多对多关系。 Hence this second definition of the many to many relationship does not need to perform database updates, because the database updates are owned by the AbstractEntity. 因此,多对多关系的第二个定义不需要执行数据库更新,因为数据库更新由AbstractEntity拥有。

In the code you have shown for saving the data, you have Event objects which are already persistent. 在显示的用于保存数据的代码中,您具有已经持久的Event对象。 You then create a new Conflict class, and add the relationships to this class. 然后,您创建一个新的Conflict类,并将关系添加到该类。 The problem is that when you persist this new class, Hibernate will not persist the relationship, because the many to many definition says that the Event objects own the database updates. 问题在于,当您持久化这个新类时,Hibernate将不会持久化该关系,因为多对多定义表明Event对象拥有数据库更新。 Hence, to fix the problem, you can either change your mapping so that it is declared in the Conflict class, and the AbstractEntity declares the counterpart using the "mappedBy" attribute, or you can persist the Conflict classes, then define the relationships using the Event classes, and update them. 因此,要解决此问题,您可以更改映射,以便在Conflict类中声明它,然后AbstractEntity使用“ mappedBy”属性声明对应项,或者可以保留Conflict类,然后使用事件类,并对其进行更新。 Something like: 就像是:

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

It turns out that the issue was not in the Hibernate annotations. 事实证明,该问题不在Hibernate批注中。 Instead, the issue resided in how I was accessing the collections in the annotated methods. 相反,问题出在我如何使用带注释的方法访问集合。

As you can see in the question, the Collection setters clear the collection and then add any items in the new collection. 正如您在问题中看到的那样,“集合”设置器清除了集合,然后在新集合中添加了所有项目。 The updated code (which works!) is shown here: 更新的代码(有效!)在此处显示:

@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;
}

and

@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;
}

For future viewers, this Hibernate configuration (mapping each side as a ManyToMany) creates two uni-directional associations: from Conflict -> AbstractEntities and from AbstractEntity -> Conflicts. 对于将来的观看者来说,这种Hibernate配置(将每一面映射为ManyToMany)会创建两个单向关联:从冲突-> AbstractEntities和从AbstractEntity->冲突。 This means that if you decide to use this configuration, you will have to be careful when adding or deleting items from the collections to make sure the joining table entries get updated to avoid foreign key constraint violations. 这意味着,如果您决定使用此配置,则在添加或删除集合中的项目时必须小心,以确保更新连接表条目,以避免违反外键约束。 For example, when deleting a Conflict, we can't just say ConflictDAO.getInstance.delete(toDelete) . 例如,删除冲突时,我们不能只说ConflictDAO.getInstance.delete(toDelete) Instead, we have to make sure the Conflict doesn't retain any associations: 相反,我们必须确保冲突不保留任何关联:

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