【问题标题】:How to use generic types into a super class using a ManyToOne relationship with hibernate如何使用与休眠的多对一关系将泛型类型用于超类
【发布时间】:2020-11-08 08:17:14
【问题描述】:

我有一个使用单表继承和其他子类的通用类:

文件,主类

文件夹扩展文件

卡片扩展文件

Abstract FileLink extends File : 扩展 File 的 FileLink 抽象类

FolderLink 扩展了 FileLink 并由一个文件夹组成。

CardLink 扩展了 FileLink 并由 Card 组成。

在我的 File 类中,我有一个具有 oneToMany 关系的 File 集合,它可以包含任何类型的 File,例如文件夹或文件夹链接或卡片链接。​​

@DiscriminatorColumn(name = "file_type", discriminatorType = DiscriminatorType.INTEGER)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class File{

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
    List<File> subFiles = new Linkedlist()

    @ManyToOne(fetch = FetchType.LAZY)
    protected File parent;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(length= 40)
    private String name;

    @Lob
    private String description;
}

一个文件夹可以有多个链接。

@Entity
@DiscriminatorValue(File.FOLDER + "")
public class Folder extends File {

    @OneToMany(mappedBy = "linkedFolder", fetch = FetchType.LAZY, targetEntity = FolderLink.class)
    protected Set<FolderLink> folderLinks = new HashSet<>();

    private Object folderAttribute;
}

卡片也一样

@Entity
@DiscriminatorValue(File.CARD+ "")
public class Card extends File {

    @OneToMany(mappedBy = "linkedCard", fetch = FetchType.LAZY, targetEntity = CardLink.class)
    protected Set<CardLink> cardLinks = new HashSet<>();

    private Object cardAttribute;
}

Filelink 类表示一个文件的链接,并没有使用太多特定的属性,只有父文件和链接的文件。

事实上,当我序列化我的子文件集合时,我希望我的链接(FolderLink 或 Cardlinks)返回链接文件的属性值。

因此,Folderlink 类应该返回 Folder 属性值,CardLink 返回 Card 属性值,FileLink 返回 File 属性值。

我的实际模型是这样制作的:

@NoArgsConstructor
public abstract class FileLink extends File {
    
    @Override
    public String getName() {
        return "Link to "+this.getLinkedFile().getName();
    }

    @Override
    public String getDescription() {
        return this.getLinkedFile().getDescription();
    }
    ...

    @JsonIgnore
    public abstract File getLinkedFile();
}

@Entity
@DiscriminatorValue(File.FOLDER_LINK + "")
public class FolderLink extends FileLink {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "folder_linked_id", insertable = false, updatable = false)
    protected Folder linkedFolder;

    @JsonIgnore
    public File getLinkedFolder() {
        return this.linkedFolder;
    }

    @Override
    public String getFolderAttribute() {
        return "Link to "+this.getLinkedFile().getFolderAttribute();
    }

    @Override
    public File getLinkedFile() {
        return this.getLinkedFolder();
    }
}


@Entity
@DiscriminatorValue(File.CARD_LINK + "")
public class CardLink extends FileLink {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "card_linked_id", insertable = false, updatable = false)
    protected Card linkedCard;

    @JsonIgnore
    public File getLinkedCard() {
        return this.linkedCard;
    }

    @Override
    public String getCardAttribute() {
        return "Link to "+this.getLinkedFile().getCardAttribute();
    }

    @Override
    public File getLinkedFile() {
        return this.getLinkedCard();
    }
}

因此,当 FolderLink 被序列化时,我可以通过 FileLink 类检索链接的文件夹属性以及在其父类(文件)中声明的属性,对于卡片链接也是如此。

但我想做的只是将 OneToMany 关系声明到 File 和 FileLink 类中:

@DiscriminatorColumn(name = "file_type", discriminatorType = DiscriminatorType.INTEGER)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class File{ 

    @OneToMany(mappedBy = "linkedFile", fetch = FetchType.LAZY, targetEntity = FileLink.class)
    protected Set<FileLink> fileLinks = new HashSet<>();

}

Filelink 类将不再是抽象的。

@NoArgsConstructor
@Entity
@DiscriminatorValue(File.FILE_LINK + "")
public class FileLink extends File {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "file_linked_id", insertable = false, updatable = false)
    protected File linkedFile;

    @Override
    public String getName() {
        return "Link to "+this.getLinkedFile().getName();
    }

    ...

    @JsonIgnore
    public File getLinkedFile(){
       return this.linkedfile;
    }
}

@Entity
@DiscriminatorValue(File.FOLDER_LINK + "")
public class FolderLink extends FileLink {

    @Override
    public String getFolderAttribute() {
        return "Link to "+((Folder)this.getLinkedFile()).getFolderAttribute();
    }

}

@Entity
@DiscriminatorValue(File.CARD_LINK + "")
public class CardLink extends FileLink {

    @Override
    public String getCardrAttribute() {
        return "Link to "+((Card)this.getLinkedFile()).getCardAttribute();
    }

}

但它不起作用。当我创建一个 FolderLink 时,我知道链接的文件是一个文件夹,但是如果我从超类中检索它,我无法将其转换为文件夹,休眠告诉我他不能将文件转换为文件夹,这很正常,因为文件不是文件夹。

有没有办法实现这个目标?我目前的实现对我来说已经足够方便了,但如果我能做得更多,那就太好了。

【问题讨论】:

  • 在设计继承层次结构时,无论何时键入B extends A,您都应该问自己一个问题:“B 可以在任何需要A 的地方使用吗?”。从这个角度来看,FolderLink 是多余的,因为您可以将Folder 分配给FileLink.linkedFile(因为Folder 扩展了File)。此外,Folder 从File 继承fileLinks 属性
  • @crizzis 是的,但是linkedFile 链接了一个文件,所以如果我序列化一个 FileLink,我只能获取 File 属性而不是 Folder 属性,这就是为什么我需要一个 FolderLink 类来这样做。我想序列化 FolderLink 或 CardLink(Card 扩展 File 也具有自己的属性),就像它是 Folder 或 Card。
  • 在这种情况下,也许您只需要一个轻量级接口来表达FolderLink 和Folder 的功能(即PhysicalFolder implements Folder 和FolderLink implements Folder;不是确定命名),不需要继承。请注意,在您的设置中,FolderLink 从 Folder 继承它从未使用过的物理属性,因为 getters 委托给 linkedFolder
  • FolderLink 和 FileLink 都可以继承自 File,或者更好的是,继承自未声明它们不需要的物理属性的父超类(例如 AbstractFilesystemItem)
  • FolderLink 不继承Folder 的属性,它不是Folder 而只是一个File,它是由一个Folder 组成的。是的,Folder 和 FolderLink 应该共享相同的接口,但这不是问题(实际上 Jackson 只需要相同的属性名称......) ***Link 类从 File 继承,因为我使用单表继承类型。

标签: java hibernate jpa data-modeling hibernate-onetomany


【解决方案1】:

正如您所提到的,您正在寻找的是 多态关联映射

hibernate 告诉我他不能将文件投射到文件夹中,这很正常,因为文件不是文件夹。

您可以通过添加另一列作为类型​​控件来定义您期望的类型。

package com.example;

import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Column;
    
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.MetaValue;

@Entity
@DiscriminatorValue(File.FILE_LINK + "")
public class FileLink extends File {

    @Any (
         metaColumn = @Column(name = "linkedFileType"),
         fetch=FetchType.LAZY
    )
    @AnyMetaDef(idType = "long", metaType = "string", metaValues = {
         @MetaValue(targetEntity = Folder.class, value = "folder"),
         @MetaValue(targetEntity = Card.class, value = "card")
    })
    @Cascade({org.hibernate.annotations.CascadeType.ALL})
    @JoinColumn(name = "file_linked_id")
    protected File linkedFile;

    @Override
    public String getName() {
        return "Link to "+this.getLinkedFile().getName();
    }

    ...

    @JsonIgnore
    public File getLinkedFile(){
       return this.linkedfile;
    }
}

【讨论】:

  • 它工作正常感谢您的帮助。我不明白的是,我不必创建任何“linkedFileType”列,但它似乎没有。
  • 您的配置中的 hibernate.hbm2ddl 模式是什么?我猜它会自动更新你的表格。
  • 好吧 ddl-auto 处于更新模式,但没有创建任何列,并且它可以正常工作,很奇怪。
  • 经过几次测试后,这些注解@Any 等似乎都不是必需的,因为在我的 File 类中,我已经为 File 的不同子类使用了鉴别器值。因此,如果将linkedFile 声明为文件,hibernate 可以使用正确的子类填充它。我不知道为什么它实际上之前不起作用,大谜!
猜你喜欢
  • 2014-02-22
  • 2023-03-17
  • 2023-03-18
  • 2023-04-06
  • 2021-09-10
  • 1970-01-01
  • 2013-05-30
  • 2015-09-12
  • 1970-01-01
相关资源
最近更新 更多