当关联表是一个对所有一对多关系时,如何实现一对多映射?

Posted

技术标签:

【中文标题】当关联表是一个对所有一对多关系时,如何实现一对多映射?【英文标题】:How to implement One-to-Many mapping when the associative table is one for all one-many relationships? 【发布时间】:2010-01-04 14:18:05 【问题描述】:

一般情况:第一个表代表一个-side,第二个表代表many-side。第三个表作为两者之间的链接。

我的情况:第一张和第二张桌子是一样的。第三个表用作具有一对多关系的所有表对之间的链接。第三个表有附加字段(字符串),其中包含有关 2 个表的信息。

小例子。假设我们有表 Project 和 Category(一个类别 --> 许多项目)。要获取所有具有类别的项目,我们需要执行下一个查询:

select project.name, category.name 
from nodeassociation, project, category 
where nodeassociation.association_name='ProjectCategory'
 and nodeassociation.source_id=project.id 
 and nodeassociation.sink_id=category.id

如何通过 JPA 指定关联名称标准?

UPD:如果 JPA 不可能,但 hibernate 可以处理它,那么没关系,hibernate 解决方案是什么?

【问题讨论】:

【参考方案1】:

建议您在association_name 列过滤的数据库中创建多个视图,然后根据这些在映射中定义多对多关系,并在代码中强制执行一对多语义。这种中间表的情况只有在 Hibernate 中使用多对多映射才能真正处理。

【讨论】:

【参考方案2】:

这通常称为qualified association。这种关联应该用Map而不是List来实现。在hibernate中没有内置对这些的支持。如果您不关心限定符,则该关联将成为可以使用 hibernate 处理的普通多对多。

但在您的情况下,这不是逻辑级别的合格关联。如果我理解得很好,一个关联表用于表示实际上是什么几个关联:

如果您可以将 db 模式更改为每个 逻辑 关联的表,问题就会消失。 使用数据库视图“模拟”具有单独的表(如 David M 所建议)是一种替代方法。每个逻辑关联创建一个视图并将其映射为多对多。

【讨论】:

【参考方案3】:

另一个可行的解决方案也许是在休眠中为NodeAssociation 提供一个多态实体。每种关联类型将有一个NodeAssociationX 实体。然后ProjectNodeAssociation 之间存在一对多关联。每个NodeAssociationX 和相应的表/实体(Category 等)都有一对多。您可以映射私有字段,以便提供公共 getter/setter,在逻辑级别提供正确视图。

public List getCategories()

     List categories = new ArrayList();
     for( NodeAssociation na : nodeAssociations )
     
           if( typeof( na ) = NodeAssociationCategory )
           
                categories.addAll( ((NodeAssociationCategory)na).getCategories() ); 
           
     
     return categories;

但这很丑陋。如果可能,请尝试其他解决方案。

【讨论】:

【参考方案4】:
@Entity
public class Category 

    private Integer id;

    @Id
    public Integer getId() 
        return this.id;
    

    private LinkBetweenOneCategoryAndManyProject linkClass = new  LinkBetweenOneCategoryAndManyProject();

    @OneToOne
    public LinkBetweenOneCategoryAndManyProject getLinkClass() 
        return this.linkClass;
    

    public void addProject(Project project) 
        getLinkClass().getProjectList().add(project);
    

    // delegates to LinkClass
    public String getAdditionalProperty() 
        getLinkClass().getAdditionalProperty();
    


现在我们的项目

@Entity
public class Project 

    private Integer id;

    @Id
    public Integer getId() 
        return this.id;
    


现在我们的 LinkBetweenOneCategoryAndManyProject 类

@Entity
public class LinkBetweenOneCategoryAndManyProject 

    private Integer categoryId;

    private String additionalField;

    List<Project> projectList;

    @Id
    public Integer getCategoryId() 
        return this.categoryId;
    

    @OneToMany
    public List<Project> getProjectList() 
        return projectList;
    

    public String additionalProperty() 
        return this.additionalField;
    


所以如果你想检索一个类别及其项目,请执行以下操作

select distinct c from Category c left join fetch c.linkedClass lc left join fetch lc.projectList

请注意,我认为 Category 和 LinkBetweenOneCategoryAndManyProject 之间的关系是 OneToOne

问候,

【讨论】:

【参考方案5】:

我找到了适合本机查询的解决方案。

简而言之:

    我为项目和类别制作了简单的映射,没有任何关系(如 OneToMany) 我将字段category 添加到项目实体并将其标记为@Transient 在 ProjectDAO 中,我使用原生查询手动将类别插入到项目中。

关于JPA Native Queries 和related jboss docs 的文章是我的起点。

【讨论】:

那么你没有通过hibernate自动持久化关系:如果你在你的项目中添加一个类别,并保存项目,你需要自己持久化类别和关联。你会失去很多休眠的力量。如果您使用它保存对象,DAO 可以完成这项工作,但是您不能简单地加载/修改实体,您需要每次加载/修改/保存。也许 EntityListener 可以以更透明的方式完成这项工作。 我将了解 EntityListener。你是对的,在你的评论出现之前我就知道这一切,但我看不到任何其他出路。我无法修改数据库架构。我的应用程序只从数据库中读取,这允许我手动更改对象。否则我可能会在应用我的解决方案之前三思而后行。

以上是关于当关联表是一个对所有一对多关系时,如何实现一对多映射?的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis的一对一关联关系映射

MyBatis的一对一关联关系映射

ThinkPHP5中模型的一对一关联关系

MyBatis_关联关系查询

使用一对一关系实现一对多关系,没有循环依赖

数据库 - 一对一对多多对多