PostgreSQL继承与JPA、Hibernate

8

我们有一个关于PostgreSQL中继承的问题,并且想将其映射为JPA实体。 我们的数据库和要映射的表如下:

CREATE TABLE Answer (
    idAnswer SERIAL,
    answerContent VARCHAR,
    idQuestion INTEGER,
    version INTEGER,

    CONSTRAINT Answer_idAnswer_PK PRIMARY KEY (idAnswer),
    CONSTRAINT Answer_idQuestion_FK FOREIGN KEY (idQuestion) REFERENCES Question(idQuestion)
);


CREATE TABLE MatchAnswer (
    matchingAnswer VARCHAR NOT NULL,
    version INTEGER,

    CONSTRAINT MatchAnswer_idAnswer_PK PRIMARY KEY (idAnswer)       
) INHERITS(Answer);


CREATE TABLE TrueFalseAnswer (
    isTrue BOOLEAN NOT NULL,
    version INTEGER,

    CONSTRAINT TrueFalseAnswer_idAnswer_PK PRIMARY KEY (idAnswer)   
) INHERITS(Answer);

在Netbeans 7.1.2中,我们使用自动工具为实体映射了它们。起初我认为只添加

就足够了。
@Entity
@Table(name = "truefalseanswer", catalog = "jobfairdb", schema = "public")
@XmlRootElement
public class Truefalseanswer extends Answer implements Serializable {
    private static final 

所以只需要继承,但它没有正常工作。对此,有什么最佳方法吗?提前感谢。


你正在使用哪个版本的PostgreSQL?这很有帮助,因为不同的版本具有不同的功能。 - Craig Ringer
PostgreSQL 9.1.4,但我们决定简化这个想法。 - Atais
3个回答

8
JPA的继承概念基于普通表,它并不真正理解PostgreSQL的表继承概念。这是使用旨在暴露最低公共特性并可移植的规范所付出的代价之一。请参阅this guide以获得有关JPA继承策略的简要概述。请注意,在新的Java 6 JavaDoc for @Inheritance中,有一个注释说:如果未为实体类层次结构指定继承注释或未指定继承类型,则使用SINGLE_TABLE映射策略。...如果您查看SINGLE_TABLE的工作方式,那么它无法满足您的需求也就不足为奇了;它期望所有子类都在一个大表中,并带有一个魔术鉴别器值。 InheritanceType.TABLE_PER_CLASS 更接近Pg的行为,但我怀疑当基类表中有每个叶子类型实体的条目时,JPA实现会有些混乱。它试图在超类查询时跨子类表执行UNION查询,这可能会产生奇怪的结果-至少是重复,如果使用UNION ALL则会产生性能问题。根据提供程序实现策略的具体方式,它可能至少部分地工作。您必须进行测试,并且结果可能相当特定于提供程序。
对于JPA的PG继承支持的真正好的实现可能需要JPA提供程序扩展,以了解PostgreSQL继承和ONLY查询的扩展的新继承策略。
如果您能说服您的JPA实现在InheritanceType.TABLE_PER_CLASS模式下使用SELECT ... FROM ONLY subclass_table,那么它应该可以与PostgreSQL继承相互操作。 它只会查看每个表中未继承的行,并像它们是普通表格一样处理它们。 您的其他非JPA代码可以继续使用继承功能。 我想您可能可以修改Hibernate的PostgreSQL方言代码来完成这项工作,但我个人不会去那里,除非我必须支持现有依赖继承的PostgreSQL模式的JPA。

好的,说出来实际上让我们看到了我们试图实现的东西,所以我们决定通过重构表格来简化这个想法...无论如何还是谢谢! - Atais

3
使用MappedSuperClass注解可以解决问题。
@MappedSuperClass
public abstract class AbstractAnswer{
    @Id
    protected Long idAnswer;

    @Column(name="answerContent")
    protected String answerContent;
}

@Entity
@Table(name="Answer")
public class Answer extends AbstractAnswer{

}

@Entity
@Table(name="MatchAnswer")
public class MatchAnswer extends AbstractAnswer{
    protected String matchingAnswer;
}

@Entity
@Table(name="TrueFalseAnswer")
public class TrueFalseAnswer extends AbstractAnswer{
    protected Boolean trueFalseAnswer;
}

有一个限制,即父表不应该有任何对自身的外键。这可以通过在每个子类中分别放置该字段而不是保留在父类中来在Java级别上解决。 - Piyush
这种方法存在一个问题,我现在才发现。如果使用 IDENTITY @Id,子类将尝试查找“subclass_id_seq”而不是“abstract_answer_id_seq”。我不知道如何解决这个问题,除非在每个子类中手动指定此序列生成器名称。 - Dalibor Filus
我在Java应用中使用了PostgreSQL继承,没有使用Hibernate,当我实现了Hibernate后,这个解决方案帮助我保持了数据库结构不变。+1 - SlimenTN

2
Hibernate中的继承与PostgreSQL中的继承无关,尽管两者都试图实现相同的目标并且看起来很相似。
原因是Hibernate以SQL标准为基础,并调整每个RDBMS的小特性。例如,虽然MySQL具有用于顺序ID的“自动增量”,但Oracle使用序列。
从历史上看,数据继承(或专业化)是通过使用特定字段的单独表进行的,这些表通过主外键将表连接在一起。在您的示例中,MatchAnswer将具有ID作为PK和FK到Answer.idAnswer。TrueFalseAnswer也是如此(ID是PK/FK到Answer.idAnswer)。
您发布的“inherits”定义在任何SQL标准中都没有定义(AFAIK),因此如果Hibernate支持此功能,我会感到惊讶,特别是因为它似乎是PostgreSQL非常特定的东西,而且似乎是一个有点实验性的功能:请查看PostgreSQL文档的“Inheritance”章节中的“注意事项”。
话虽如此,我建议保持数据健康,根据最佳关系模型实践进行映射。然后,在Hibernate映射中,可以在有意义的地方表达数据继承。

它并不是极其实验性的,但确实是一种有限特性。我不会使用JPA从PostgreSQL继承 - 或者除了它确实有用的少数情况之外的任何事情。这个功能大多用于使表分区对应用程序透明,即使在那里也存在一些奇怪的小问题。 - Craig Ringer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接