没有主键或连接表的休眠多对一关系

Posted

技术标签:

【中文标题】没有主键或连接表的休眠多对一关系【英文标题】:Hibernate Many-To-One Relationship without Primary Key or Join Table 【发布时间】:2013-06-07 17:25:16 【问题描述】:

问题

首先我想说我意识到数据库结构很糟糕,但我现在无法更改它。

话虽如此,我需要在 Hibernate (4.2.1) 中创建一个不涉及主键的一对多双向关系(仅关系的“父”端的唯一键) 并且没有连接表。表示这种关系的外键是从“子”到“父”的反向指针(见下文)。我已经搜索并尝试了各种不同的注释配置,但没有成功。我要求的可能吗?

数据库

GLOBAL_PART

CREATE TABLE "GLOBAL_PART" (    
    "GLOBAL_PART_ID" NUMBER NOT NULL,
    "RELEASES" NUMBER,
    CONSTRAINT "GLOBAL_PART_PK" PRIMARY KEY ("GLOBAL_PART_ID"),
    CONSTRAINT "GLOBAL_PART_RELEASES_UK" UNIQUE ("RELEASES")
);

PART_RELEASE

CREATE TABLE "PART_RELEASE" (
    "PART_RELEASE_ID" NUMBER NOT NULL,
    "COLLECTION_ID" NUMBER,
    CONSTRAINT "PART_RELEASE_PK" PRIMARY KEY ("PART_RELEASE_ID"),
    CONSTRAINT "GLOBAL_PART_RELEASE_FK" FOREIGN KEY ("COLLECTION_ID")
        REFERENCES "GLOBAL_PART" ("RELEASES") ON DELETE CASCADE ENABLE
);

参考:

PART_RELEASE                 GLOBAL_PART
-------------------          ------------
PART_RELEASE_ID (PK)         GLOBAL_PART_ID (PK)
COLLECTION_ID -------------> RELEASES (UK)

Java

GlobalPart.java

@Entity
@Table(name = "GLOBAL_PART")
@SequenceGenerator(name = "SEQUENCE_GENERATOR", sequenceName = "GLOBAL_PART_SEQ")
public class GlobalPart extends ModelBase implements Serializable 

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "GLOBAL_PART_ID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
    private Long globalPartId;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "COLLECTIONGID")
    private Set<PartRelease> releases;

    public Long getGlobalPartId() 
        return globalPartId;
    

    public void setGlobalPartId(Long globalPartId) 
        this.globalPartId = globalPartId;
    

    public Set<PartRelease> getReleases() 
        return releases;
    

    public void setReleases(Set<PartRelease> releases) 
        this.releases = releases;
    


PartRelease.java

@Entity
@Table(name = "PART_RELEASE")
@SequenceGenerator(name = "SEQUENCE_GENERATOR", sequenceName = "PART_RELEASE_SEQ")
public class PartRelease extends ModelBase implements Serializable 

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "PART_RELEASE_ID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
    private String partReleaseId;

    @ManyToOne
    @JoinColumn(name = "RELEASES")
    private GlobalPart globalPart;

    public String getPartReleaseId() 
        return partReleaseId;
    

    public void setPartReleaseId(String partReleaseId) 
        this.partReleaseId = partReleaseId;
    

    public GlobalPart getGlobalPart() 
        return globalPart;
    

    public void setGlobalPart(GlobalPart globalPart) 
        this.globalPart = globalPart;
    


任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

首先,在双向关联中,总是有一个定义映射的所有者端和一个以mappedBy 属性的存在为标志的反向端。

在 OneToMany 关联中,所有者方始终是多方(在您的情况下为 PartRelease)。

此外,默认情况下,连接列引用它所引用的实体的 ID。由于这不是您想要的,您必须指定引用的列名。

当然,必须映射 RELEASES 列:

public class GlobalPart extends ModelBase implements Serializable 

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "globalPart")
    private Set<PartRelease> partReleases;

    @Column(name = "RELEASES")
    private Long releasesNumber;


public class PartRelease extends ModelBase implements Serializable 
    @ManyToOne
    @JoinColumn(name = "COLLECTION_ID", referencedColumnName = "RELEASES")
    private GlobalPart globalPart;


the documentation 中很好地描述了关联。你应该阅读它。

【讨论】:

完美!非常感谢!我最初走这条路,但我错过了映射到RELEASES 列的releaseNumber 属性。最终我说服自己我做错了(通过误读 MappingException)并采用了问题中提到的方法。再次感谢!【参考方案2】:

当您需要在父端使用非主键列映射@ManyToOne 时,您必须使用@JoinColumn 注释的referencedColumnName 属性。

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
    name = "RELEASES", 
    referencedColumnName = "COLLECTIONGID"
)

我将FetchType.LAZY 用于@ManyToOne,因为默认情况下使用FetchType.EAGER 获取,这对性能非常不利。

【讨论】:

请假设我在双向单机关联中使用了最佳实践。并如您所说使用了非主键连接列。现在我想通过一个 id 来获取一个简单的 ManyToOne 实体。但是休眠日志在另一侧实体上显示了一个额外的选择,其中引用的列名在哪里。为什么?而当我使用主键连接列时,这个额外的选择不存在。

以上是关于没有主键或连接表的休眠多对一关系的主要内容,如果未能解决你的问题,请参考以下文章

休眠:多对一关系失败

如何让jpa休眠创建具有多对一但没有外键的实体

mybatis一对一关联关系映射

MyBatis - 关联查询

django--ORM表的多对一关系

可空多对一的休眠默认连接