【问题标题】:Why @OneToMany does not work with inheritance in Hibernate为什么@OneToMany 不适用于 Hibernate 中的继承
【发布时间】:2010-09-07 11:21:39
【问题描述】:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Problem {
    @ManyToOne
    private Person person;
}

@Entity
@DiscriminatorValue("UP")
public class UglyProblem extends Problem {}

@Entity
public class Person {
    @OneToMany(mappedBy="person")
    private List< UglyProblem > problems;
}

我认为我想做什么已经很清楚了。我希望 @ManyToOne 人被 UglyProblem 类继承。但是会有一个例外说:“在 UglyProblem 类中找不到这样的属性 (mappedBy="person")”。

我找到的只是this。我找不到 Emmanuel Bernard 的帖子,解释了这背后的原因。


不幸的是,根据 Hibernate 文档“未映射为 @MappedSuperclass 的超类的属性将被忽略。”

我认为这意味着如果我有这两个类:

public class A {
    private int foo;
}

@Entity
public class B extens A {
}

然后字段foo 将不会映射到B 类。这是有道理的。但如果我有这样的事情:

@Entity
public class Problem {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String name;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}

@Entity
public class UglyProblem extends Problem {

private int levelOfUgliness;

public int getLevelOfUgliness() {
    return levelOfUgliness;
}

public void setLevelOfUgliness(int levelOfUgliness) {
    this.levelOfUgliness = levelOfUgliness;
}
}

我希望 UglyProblem 类有文件 idname 并且两个类都使用同一个表进行映射。 (事实上​​,这正是发生的事情,我刚刚再次检查)。我有这张桌子:

CREATE TABLE "problem" (
    "DTYPE" varchar(31) NOT NULL,
    "id" bigint(20) NOT NULL auto_increment,
    "name" varchar(255) default NULL,
    "levelOfUgliness" int(11) default NULL,
    PRIMARY KEY  ("id")
) AUTO_INCREMENT=2;

回到我的问题:

我希望 @ManyToOne 人被 UglyProblem 类继承。

我希望这是因为所有其他映射字段都是继承的,我认为没有任何理由为多对一关系设置此例外。


是的,我看到了。事实上,我在我的案例中使用了只读解决方案。但我的问题是“为什么……” :)。我知道hibernate团队的成员给出了解释。我找不到它,这就是我问的原因。

我想找出这个设计决策的动机。

(如果你对我是如何面对这个问题感兴趣的话:我继承了一个使用 hibernate 3 构建的项目。它是 Jboss 4.0。一些东西 + hibernate 已经存在(你可以一起下载)。我正在将此项目移动到Jboss 4.2.2,我发现有“@OneToMany mappedBy”的继承映射,它在旧设置上运行良好......)

【问题讨论】:

  • 您应该在 9 月 4 日添加有关您的编辑的更多详细信息。在 @OneToMany(mappedBy="person") 映射中指定 List 而不是仅 List 会改变问题的性质,因为我认为我们之前假设您想将 Person 映射到问题列表(不是 Ugl

标签: java hibernate inheritance orm


【解决方案1】:

在我的例子中,我想使用 SINGLE_TABLE 继承类型,所以不能使用 @MappedSuperclass。

虽然不是很干净,但可行的方法是将 Hibernate 专有的 @Where 子句添加到 @OneToMany 关联以强制查询中的类型:

@OneToMany(mappedBy="person")
@Where(clause="DTYPE='UP'")
private List< UglyProblem > problems;

【讨论】:

  • 对于 eclipseLink 或 JPA 的 @Where 的替代品将不胜感激
  • 您好 Odys,在 EclipseLink 中您根本不需要指定任何内容。 Hibernate 通常会强制您在 oneToMany 注释中指定 targetEntity=Person.class。因此,当您拥有带有 field1 的实体时。丑陋的人;丑陋的人;和 field2 BeutifulPerfoson beutifulPerson;在 eclipselink 中,他自动知道查看您的 BeuftifulPerson @DescriminatorValue("PRETTY_PERSON") 并获取正确的内容。 Hibernate 不知道这一点,并强制您使用@WHERE 子句。 Eclipselink 可以智能地分析类层次结构并解析 oneToMany 映射。
  • 在 Ecliipselink 中,只需确保作为类层次结构一部分的实体都有自己的相应实体; @DiscriminatorValue("BEAUTIFUL_PERON") 判别器并添加到基类中,如果您的实体类似于:@DiscriminatorColumn(name = "ENTITY_CLASS", descriminatorType=string) 并且您可以轻松地使用 @Inheritance(SINGLE_TABLE) 与 Hibernate,您非常卡住在这一点上,您需要指定一个 WHERE 子句,并为层次结构中的每个子元素使用 ENTITY_CLASS 持续维护谓词。所以这里 eclipselink 遥遥领先。
  • @Where 在我尝试两次映射集合时帮助了我。我这样做是为了将具有特定属性的子实体分成两个集合。示例:@OneToMany @JoinColumn(name = 'FLEET_ID') @Where(clause = "COLOR='blue'") Set&lt;Car&gt; blueCars;
【解决方案2】:

我认为这是 Hibernate 团队做出的明智决定。他们可以不那么傲慢,并清楚说明为什么要以这种方式实施,但这正是 Emmanuel、Chris 和 Gavin 的工作方式。 :)

让我们试着理解这个问题。我认为您的概念是“撒谎”。首先,您说许多问题有关。但是,然后你说一个 Person 有许多 UglyProblem(并且与其他 Problem 无关)。那个设计有问题。

想象一下它将如何映射到数据库。你有一个单一的表继承,所以:

          _____________
          |__PROBLEMS__|          |__PEOPLE__|
          |id <PK>     |          |          |
          |person <FK> | -------->|          |
          |problemType |          |_________ |
          -------------- 

如果 problemType 等于 UP,hibernate 将如何强制数据库使 Problem 仅与 People 相关?这是一个非常难以解决的问题。所以,如果你想要这种关系,每个子类都必须在它自己的表中。这就是@MappedSuperclass 所做的。

PS.: 抱歉画的太丑了 :D

【讨论】:

    【解决方案3】:

    不幸的是,根据 Hibernate documentation“未映射为 @MappedSuperclass 的超类的属性将被忽略。”我也遇到了这个。我的解决方案是通过接口而不是实体 bean 本身来表示所需的继承。

    在您的情况下,您可以定义以下内容:

    public interface Problem {
        public Person getPerson();
    }
    
    public interface UglyProblem extends Problem {
    }
    

    然后使用一个抽象超类和两个实体子类来实现这些接口:

    @MappedSuperclass
    public abstract class AbstractProblemImpl implements Problem {
        @ManyToOne
        private Person person;
    
        public Person getPerson() {
            return person;
        }
    }
    
    @Entity
    public class ProblemImpl extends AbstractProblemImpl implements Problem {
    }
    
    @Entity
    public class UglyProblemImpl extends AbstractProblemImpl implements UglyProblem {
    }
    

    另外一个好处是,如果您使用接口而不是实现这些接口的实际实体 bean 进行编码,那么以后更改底层映射会更容易(破坏兼容性的风险更小)。

    【讨论】:

      【解决方案4】:

      我认为你需要用 @MappedSuperclass 而不是 @Entity 来注释你的 Problem 超类。

      【讨论】:

        【解决方案5】:

        我想出了如何解决 OneToMany mappedBy 问题。

        在原始帖子的派生类 UglyProblem 中。回调方法需要在派生类而不是父类中。

        @Entity
        @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
        @ForceDiscriminator
        public class Problem {
        
        }
        
        @Entity
        @DiscriminatorValue("UP")
        public class UglyProblem extends Problem {
            @ManyToOne
            private Person person;
        }
        
        @Entity
        public class Person {
            @OneToMany(mappedBy="person")
            private List< UglyProblem > problems;
        }
        

        至少找到了使用 Hibernate 的秘诀。 http://docs.jboss.org/hibernate/stable/annotations/api/org/hibernate/annotations/ForceDiscriminator.html@ForceDiscriminator 使 @OneToMany 尊重鉴别器

        需要休眠注释。

        【讨论】:

        • 根据JavaDocInheritanceType.SINGLE_TABLE是默认的
        【解决方案6】:

        在我看来,@JoinColumn 至少应该提供一个选项来将 @DiscriminatorColumn = @DiscriminatorValue 应用于 SQL 的“where”子句,尽管我更希望这种行为成为默认行为。

        我很惊讶在 2020 年这仍然是一个问题。 由于这种对象设计模式并不罕见,我认为 JPA 还没有在规范中涵盖这个简单的特性是一种耻辱,因此仍然迫使我们寻找丑陋的解决方法。

        为什么一定要这么难?这只是一个额外的 where 子句,是的,我确实为 @JoinColumn、@DiscriminatorColumn 组合准备了一个数据库索引。

        .i.. JPA

        引入您自己的自定义注释并编写生成本机查询的代码。这将是一个很好的锻炼。

        【讨论】:

          猜你喜欢
          • 2016-11-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-14
          • 2020-03-30
          • 1970-01-01
          相关资源
          最近更新 更多