简体   繁体   English

如何在java中使用hibernate正确实现owner-owned-owner2关联?

[英]How to properly implement a owner-owned-owner2 association in java with hibernate?

I have looked for information on how to implement the following association in hibernate and although the hibernate manual is very thorough, I haven't found the following use case addressed. 我已经查找了有关如何在hibernate中实现以下关联的信息,虽然hibernate手册非常彻底,但我还没有找到以下用例。

I have a requirement to have an association between to entities, where the association has several attributes besides the foreign keys to the associated entities. 我要求在实体之间建立关联,其中关联除了关联实体的外键之外还有几个属性。 The specifications for the relation are: 关系的规范是:

  • A Container is associated to Contained through Position. Container通过Position关联。
  • A Position can not exist without both a Container and a Contained item. 如果没有Container和Contained项,则不能存在Position。
    • Hence, if either the Container or the Contained item is deleted, the Position should be deleted. 因此,如果删除Container或Contained项,则应删除Position。
  • A Container can contain 0 or more Positions. Container可包含0个或多个Position。
  • A Position refers to one and only one Contained item. 位置是指一个且仅包含一个包含项目。

I have managed to configure most of the requirements through annotations and it works out quite nicely, except for cascading the delete from the Contained item. 我已经设法通过注释配置了大部分需求,并且它很好地工作,除了从Contained项目级联删除。 I have a work around to do this described below, but I would like to configure this action through annotations to have the database automatically do the work in order to have a more robust referential integrity. 我有一个工作要做,如下所述,但我想通过注释配置此操作,让数据库自动完成工作,以便具有更强大的参照完整性。

This is the mapping that I have so far: 这是我到目前为止的映射:

@Entity
public class Container
{
    @OneToMany(cascade = CascadeType.ALL,
               orphanRemoval = true,
               fetch = FetchType.EAGER)
    @JoinColumn(name = "container_fk")
    public Set<Position> getPositions() { return this.positions; }
    public void setPositions(final Set<Position> positions) { this.positions = positions; }
    private Set<Position> positions;
    ...
}

@Entity
public class Position
{
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "container_fk", insertable = false, updatable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    public Container getContainer() { return this.container; }
    public void setContainer(Container container) { this.container = container; }
    private Container container;

    @NaturalId
    @OneToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "contained_fk")
    public Contained getContained() { return this.contained; }
    public void setContained(Contained contained) { this.contained = contained; }
    private Contained contained;

    // other attributes are owned by this relationship
    // (i.e., they don't make sense in either Container or Contained.
    ...
}

To delete the Position, when deleting the Contained item the following is implemented in code in a ContainedDao (presented without exception handling and session management is done in the base Dao class for simplicity): 要删除位置,在删除Contained项时,以下内容在ContainedDao中的代码中实现(无异常处理,并且为了简单起见,在基本Dao类中进行了会话管理):

@Repository
@Transactional(rollbackFor = Throwable.class)
public class ContainedDao extends TransactionalDao<Contained>
{
    public void delete(String id)
    {
        final Session session = getSession();

        // If there is a Position associated to the Contained item delete it,
        // and remove it from any Container collection.
        Position position = (Position) session.createCriteria(Position.class)
                                              .createCriteria("contained")
                                              .add(Restrictions.eq("id", id))
                                              .uniqueResult();
        if (position != null)
        {
            position.getContainer().getPositions().remove(position);
            session.delete(position);
        }

        // Delete the Contained item.
        Contained object = session.load(Contained.class, id);
        session.delete(contained);
    }
}

What I would like to do is to somehow configure through annotations so that the ContainedDao.delete method is simplified to a simple: 我想做的是以某种方式通过注释进行配置,以便将ContainedDao.delete方法简化为一个简单的方法:

// Delete the Contained item.
Contained object = session.load(Contained.class, id);
session.delete(contained);

Is this possible? 这可能吗? Or is my current solution the best I can get? 或者我现在的解决方案是我能得到的最好的解决方 Is there a better way to approach this? 有没有更好的方法来解决这个问题? Note that a key factor here is that Position containes additional attributes; 请注意,这里的关键因素是Position包含其他属性; otherwise, I would have configured the association between Container/Contained directly and let hibernate manage the association. 否则,我会直接配置Container / Contained之间的关联,让hibernate管理关联。

When you say "A Position refers to one and only one Contained item" , do you mean a one-to-one relationship (as opposed to a many-to-one )? 当你说“的位置指的是一个和唯一一个包含的项目”,你的意思是一比一的关系(而不是多到一 )? If that is the case, then I suppose what you really want to express here is that Position is-a Contained entity. 如果是这种情况,那么我想你真正想要表达的是Position 是一个包含的实体。

The single difference between a one-to-one and an is-a relationship is in the life-cycle of the relationship. 一对一的和之间的单差是-的关系是在关系的生命周期。 In a one-to-one relationship the Contained instance to which the Position points to might change. 一对一关系中,Position指向的Contained实例可能会更改。 An is-a relationship is more restricted: the relationship is essentially the identity of the Position itself, therefore changing it is not allowed. is-a关系受到更多限制:关系本质上是Position本身的身份,因此不允许更改它。

To implement an is-a relationship you need three things: 要实现is-a关系,您需要三件事:

  1. The foreign-key from Position to Contained should also be the primary-key of Position. 从Position到Contained的外键也应该是Position的主键 Since primary keys don't change (I am allowing myself the luxurious presumption here that you are using an auto-generated surrogate key on Contained) the is-a relationship cannot be updated either. 由于主键不会改变(我允许自己在这里使用自动生成的替代密钥 ),因此无法更新is-a关系。 When you delete a row from Contained, you have to remove any referencing Positions too (there can be at most one). 从Contained中删除行时,您还必须删除任何引用位置(最多只能有一个)。
  2. The Position class should extend the Contained class. Position类应该扩展 Contained类。 As above with the database model, an extends declaration will stay as such for the whole life-cycle of a Position object. 如上所述,对于数据库模型, extends声明将在Position对象的整个生命周期中保持不变。 When you delete the Contained object, your Position is gone too (they are the same object in memory, only accessed through different types of references) 当你删除Contained对象时,你的位置也消失了(它们是内存中的同一个对象,只能通过不同类型的引用访问)
  3. In your Hibernate configuration specify Position to be a joined-subclass of Contained. 在Hibernate配置中,将Position指定为Contained的连接子类

Then following DAO code to delete a Contained instance would also cascade to Position: 然后按照DAO代码删除一个Contained实例也会级联到Position:

public void deleteContained(String id)
{
    Contained c = (Contained) session.createCriteria(Contained.class)
                                     .add(Restrictions.eq("id", id))
                                     .uniqueResult();

    // Removes associated Position too, if exists
    session.delete(c);
    // This would fail now:
    // session.load(Position.class, id);
}

Also, deleting a Position instance would cascade to Contained: 此外,删除Position实例将级联为Contained:

public void deletePosition(String id)
{
    Position p = (Position) session.createCriteria(Position.class)
                                   .add(Restrictions.eq("id", id))
                                   .uniqueResult();

    // Removes associated Contained too
    session.delete(p);
    // This would fail now:
    // session.load(Contained.class, id);
}

PS A one-to-one relationship is a bogus concept anyway, an OOP illusion. PS无论如何,一对一的关系是一种虚假的概念,一种OOP错觉。 Instead of using a one-to-one, such relationships should either be properly restricted to an is-a , or reconsidered as a many-to-one . 这种关系应该被适当地限制为is-a ,或者被重新考虑为多对一 ,而不是一对一使用 You can substitute most of my points about one-to-one with many-to-one above. 你可以用上面的多对一替换大多数关于一对一的点。

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

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