为什么@OneToMany在Hibernate中不能与继承一起使用
@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")"。
我发现的就是这个。我找不到Emmanuel Bernard的帖子来解释其背后的原因。
Unfortunately, according to the Hibernate documentation "Properties from superclasses not mapped as @MappedSuperclass are ignored."
好吧,我认为这意味着如果我有以下两个类别:
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具有文件id和名称,并且两个类都将使用同一表进行映射。 (实际上,这正是发生的情况,我刚刚再次检查了一下)。我有这张桌子:
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;
回到我的问题:
I expect @ManyToOne person to be inherited by UglyProblem class.
我希望这是因为所有其他映射字段都是继承的,并且我看不出有任何理由要对ManyToOne关系进行此例外。
是的,我看到了。实际上,我在案例中使用了只读解决方案。但是我的问题是"为什么……" :)。我知道冬眠团队的一个成员给出了解释。我找不到它,这就是为什么我问。
我想找出这个设计决定的动机。
(如果我们感兴趣,我是如何面对这个问题的:我继承了一个使用hibernate 3构建的项目。它是Jboss 4.0。某物+ hibernate已经存在(我们将全部下载到一起)。我正在将该项目移至Jboss 4.2. 2,我发现继承了" @OneToMany mappingBy"的映射,并且在旧的设置下效果很好...)
解决方案
回答
我认为我们需要使用@MappedSuperclass而不是@Entity来注释Problem超类。
回答
不幸的是,根据Hibernate文档,"未映射为@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进行编码,则以后可以更轻松地更改基础映射(减少破坏兼容性的风险)。
回答
我认为这是Hibernate团队做出的明智决定。他们可能不太自大,并明确说明了为什么要采用这种方式,但这正是伊曼纽尔,克里斯和加文的工作方式。 :)
让我们尝试了解问题所在。我认为概念是"说谎"的。首先,我们说许多问题都与人有关。但是,然后我们说一个人有很多UglyProblems(与其他问题无关)。该设计有问题。
想象一下它将如何映射到数据库。我们具有单个表继承,因此:
_____________ |__PROBLEMS__| |__PEOPLE__| |id <PK> | | | |person <FK> | -------->| | |problemType | |_________ | --------------
如果其问题类型等于UP,休眠将如何强制数据库使问题仅与People有关?这是一个很难解决的问题。因此,如果我们想要这种关系,则每个子类都必须位于其自己的表中。这就是@MappedSuperclass所做的。
PS .:对不起,丑图:D
回答
我想出了如何解决OneToMany MappingBy问题。
在原始帖子的派生类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成为判别器
需要休眠注释。