【问题标题】:Hibernate polymorphism: instantiating the right classHibernate 多态性:实例化正确的类
【发布时间】:2009-04-16 19:58:15
【问题描述】:

我是 hibernate 的新手,你很快就会看到。如果这个问题有一个简单的答案,我深表歉意,但我对所有术语都不够熟悉,无法轻松找到它。

假设我有一个基类“A”和一个子类“B”,我正在使用 Hibernate 进行映射,可能使用表每个子类策略。基类不是抽象的。所有 B 都是 As,但并非所有 As 都是 B。这反映在数据库中,其中表 B 引用表 A。

好的,现在假设我有一个显示 A 对象列表的程序。用户可以选择任何 A 对象并被带到屏幕上进行修改...但是,如果 A 对象也是 B,则屏幕将允许用户修改 B 而不仅仅是 A。

我到底该如何处理这个问题?

注意:我不是在问如何确定一个对象是什么类。我要问的是如何让休眠状态返回正确类的对象列表。

【问题讨论】:

  • 您是在问如何进行继承映射? hibernate.org/hib_docs/v3/reference/en-US/html/inheritance.html您能否更具体地说明您在使用文档时遇到的困难?
  • 我读过一些关于继承映射的文章,并认识到“每个子类的表”策略可能是合适的。我不明白的是,查询超类将返回子类的对象......在我短暂的搜索过程中我没有找到代码示例。非常感谢!

标签: java hibernate orm


【解决方案1】:

我再次为这个问题道歉。我对hibernate的工作方式感到非常惊讶,它真的很酷。我不认为它会自动完成所有这些,我什至不确定我想问什么。当我回复 cmets 时,我开始在脑海中细化这个问题,然后能够找到我正在寻找的答案。感谢所有提供帮助的人。

答案是:hibernate 会自动执行此操作。

假设您的数据库中有一个主键“id”的表 A,以及一个引用表 A 的主键“a_id”的表 B。

所以你创建了以下类(缩写):

public class A {
  private String aProperty;
  // ... getter and setter, etc
{

public class B extends A {
  private String bProperty;
  // ... getter and setter, etc
}

然后像这样映射它们:

<hibernate-mapping>
    <class name="A" table="a" catalog="testokdelete">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <generator class="identity" />
        </id>
        <property name="aProperty" column="a_property"/>
        <joined-subclass name="B" table="b">
            <key column="a_id"/>
            <property name="bProperty" column="b_property"/>
        </joined-subclass>
    </class>
</hibernate-mapping>

您可以使用简单的“来自 A”查询返回 A 和 B 对象,如下代码所示:

Query query = session.createQuery("from A");
List<Object> objects = query.list();
for (Object object: objects) {
    System.out.print("A property: ");
    System.out.print(((A)object).getAProperty());
    System.out.print(", B property: ");
    System.out.println( (object.getClass() == B.class) ? ((B)object).getBProperty() : "not a B");
}

它所做的只是使用“来自 A”的查询返回一个对象列表,然后遍历它们,从我们的 A 类中打印出 aProperty,如果该类是 B 类型,则从我们的 B 类中打印出 bProperty。

这种情况下的休眠查询是自动多态的,并会在适当的时候给你一个 B 对象。

【讨论】:

  • 是的,Hibernate 就是这样做的。但是使用 (object.getClass() == B.class) 吗? ((B)object) 是 RTTI。您不应该这样做,这样做表明您的类设计不佳。
  • 这看起来像简单的调试代码。这并不一定意味着会以这种方式使用“真实”类。但是,有时需要 RTTI,在这些情况下,测试的正确习惯用法是 object instanceof B,而不是 object.getClass() == B.class
【解决方案2】:

您可以将 RTTI 与 instanceof 一起使用,但这不是面向对象的。

相反,给基类 A 一个方法,该方法与 B 的额外功能有关,例如,如果 ACustomer 并且 BPreferredCustomer,则该方法可能是 @ 987654327@.

A中,该方法返回false,在B中返回true。

需要注意的是,我们并不是在询问一个对象是否属于某个类。我们在问一个业务问题,问一个对象是否可以做某事。这是一个微妙但重要的区别。

特别是,这意味着当您添加更多Customer 子类时,您的以下代码不必更改,只要每个子类如实回答问题isPreferredCustomer()

在您的代码中:

if( a.isPreferredCustomer() ) {
  showPreferredCustomerPage( a) ;
else { 
  show CustomerPage(a);
}

您可能认为给Customer 提供一个showPage() 方法会更好,但这会将您的模型与视图绑定得太紧。

相反,您将上述代码放在某个 ViewDispatcher 类中,该类可能与 Customer 及其子类正交变化。

例如,您可能有多个ViewDispatcher 子类,其中一些关心PreferredCustomers,而另一些则不关心。

【讨论】:

  • 最好不要在业务逻辑中混淆类型 - 以前从未真正考虑过这一点。 +1
  • 但在这种情况下,A 对 B 一无所知。在类或被映射的表中,没有 A 的属性表明 A 是否也是 B。
  • 子类化的一点是能够向类添加额外的功能,而无需修改基类。该解决方案将 A 与 B 的详细信息混淆了。当您获得具有不同客户类型的 C 时会发生什么。现在你需要修改 isPreferredCustomer 并且它不是布尔值。
  • null,你的观点很好,我不想忽视它。你有没有更好的方法来做到这一点?
  • 您正在使用 Hibernate。假设您设置了 Hibernate XML/Annotations/whatever,Hibernate 将为您进行查询。
【解决方案3】:

好吧,我假设您的类已正确映射。如果你这样做了,我认为你应该能够做到以下几点:

public List<A> getAllClassA(){
      Session session = HibernateUtil.getSession();
      Criteria criteria = session.createCriteria(A.class);

      List<A> ret = criteria.list();

      return ret;
}

如果您不熟悉 Criteria,那么它很容易上手。这段代码的 sn-p 将从数据库中检索 A 类的所有对象。如果您正确映射了它,我认为 Hibernate 还将维护您的 A 对象的 B 属性,如果它们实际上是 B 对象(对不起,如果这令人困惑)。

如果这是您正在寻找的,或者您是否需要 HibernateUtil 的代码,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-05
    • 2012-10-13
    • 2021-07-09
    相关资源
    最近更新 更多