2011-09-04 61 views
5

我已经查找了如何在休眠中实现以下关联的信息,虽然hibernate手册非常详尽,但我还没有找到解决以下用例。如何在java中用hibernate正确实现owner-owned-owner2关联?

我有一个要求与实体之间的关联,其中关联除了关联实体的外键之外还有几个属性。关系的规格如下:

  • 容器与通过位置包含关联。
  • 没有Container和Contained项目的情况下,职位不能存在。
    • 因此,如果Container或Contained项目被删除,则应删除Position。
  • 容器可以包含0个或更多位置。
  • 一个位置是指一个且只有一个包含的项目。

我已经成功地通过注释配置了大部分需求,除了级联从Contained项目中删除外,它工作得非常好。我有一个工作要做到这一点下面描述,但我想通过注释配置此操作,让数据库自动执行工作,以便具有更强大的参照完整性。

这是映射的是我到目前为止有:

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

要删除的位置,删除包含的项目下面是在代码实现的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); 
    } 
} 

我想要做的就是以某种方式通过注释配置,以便让ContainedDao.delete方法被简化为一个简单的:

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

这可能吗?或者,我现在的解决方案是最好的?有没有更好的方法来解决这个问题?请注意,这里的关键因素是Position包含附加属性;否则,我将直接配置Container/Contained之间的关联并让hibernate管理关联。

+0

有没有什么办法可以澄清这个问题,以便评估我是否找到了我所希望的最佳解决方案,或者是否有办法让数据库为提高参照完整性做好工作? – miguelf

+0

你写过一个职位只有一个Contained。在其生命周期中,Position实例是否有可能指向两个或更多个不同的Contained项目? (即你是否打算将外键更新为Contained in Position) –

+0

不,我把它作为一个不可变属性。因此,如果您希望指向另一个包含的位置,您将删除该位置并创建一个新位置。具有position元素的主要原因是由于语义以及因为它允许在延迟加载包含的对象时获取这些属性而保持与关联相关的属性。 – miguelf

回答

1

当你说“的位置指的是一个和唯一一个包含的项目”,你的意思是一个一到一个关系(而不是一个多到一个)?如果是这样的话,那么我想你真正想表达的是是-al Contained entity。

一个一到一个是-一个关系之间的单差的关系的生命周期。在一对一关系中,位置指向的Contained实例可能会更改。一个is-a关系更受限制:关系本质上是位置本身的身份,因此不允许改变它。

要实现是,一个关系需要三样东西:

  1. 从position 外键载也应位置的主键。由于主键不变(我允许自己在这里的豪华推定,你正在使用一个自动生成替代关键对Contained)is-a关系也不能更新。当你从Contained中删除一行时,你必须删除任何引用的Positions(最多可以有一个)。
  2. Position类应该扩展 Contained类。如上所述的数据库模型,扩展为声明将保留在Position对象的整个生命周期中。当你删除Contained对象时,你的位置也不见了(它们是内存中的同一个对象,只能通过不同类型的引用访问)
  3. 在你的Hibernate配置中,指定Position是一个joined-subclass Contained。

然后下面的DAO代码删除被包含的情况下也将级联到您的位置:

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

此外,删除位置实例将级联包含:

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

附:无论如何,一对一的关系是一个虚假的概念,一个OOP幻觉。此类关系不应使用一对一的方式,而应适当限制为,或者重新考虑为多对一的。你可以用上面的多对一替换大部分我的观点。