问题:使用鉴别器的每个子类的表

时间:2010-10-05 13:11:08

标签: hibernate

问题摘要

Hibernate假设每当有人使用子类时,无论提供了哪个表名,都应在类层次结构的顶部根目录中创建所有列。有人可以解释其背后的原因吗?如果有办法绕过这个代码,并以某种方式避免在事务表中创建invoice_id ???

详情

我的域模型中有一个非常深的类层次结构,并希望使用discriminator标记来支持继承。

最初我使用join-subclass映射我的类,但是所涉及的表有数十万条记录,并且它最终成为性能瓶颈。所以本质上我想要在没有触及域的情况下展平我的表结构,这就是为什么我们转向鉴别器标签。

我的hibernate映射看起来像(为了简单起见删除了额外的列)

交易映射

<class name="Transaction" table="transaction" abstract="true">
   ...
    <discriminator column="transaction_type" type="string"/>
</class>

CashBasedTrsansaction 映射

<subclass name="CashBasedTransaction" extends="Transaction" discriminator-value="CASH">
    <join table="cash_based_transaction">
        <key column="id" />
    </join>
    <subclass discriminator-value="BILLING" name="BillingTransaction">
        <join table="billing_transaction">
            <key column="id" />
            <many-to-one name="invoice" column="invoice_id" cascade="all" access="field" lazy="false">
            </many-to-one>
        </join>
        <subclass name="ChildBillingTransaction" discriminator-value="UPT">
            <join table="billing_transaction">
                <key column="id" />
                ...
            </join>
        </subclass>
        <subclass abstract="true" name="AnotherChildOfBillingTransaction" discriminator-value="LPT">
             <subclass name="SuperChildOfBillingTransaction" discriminator-value="OCLPT">
                 <join table="billing_transaction">
                     <key column="id" />
                     ...
                 </join>
             </subclass>
             <subclass name="AnoherSuperChildOfBillingTransaction" discriminator-value="SLPT">
                 <join table="billing_transaction">
                     <key column="id" />
                     ...
                 </join>
             </subclass>
         </subclass><!--End Of AnotherChildOfBillingTransaction-->
     </subclass><!--End Of BillingTransaction-->
</subclass><!--End Of CashBasedTransaction-->

发票映射

<class name="Invoice" table="invoice">
    ...
    <bag name="billingTransactions" access="field" cascade="all" inverse="true" table="billing_transaction">
        <key column="invoice_id" />
        <one-to-many class="BillingTransaction" />
    </bag>
</class>

我想要实现的目标:我想在billing_transaction之后展平表结构。换句话说,我想在数据库中只有三个表

  • 交易
  • cash_based_transaction
  • billing_transaction(此表应在展平所有子类后保留所有列)

P.S:请注意,我想要缩小表格结构,而不是从聚合根(读取事务),但在我的类层次结构中的某个地方,在这种情况下为billing_transaction。

问题:Hibernate在事务表中创建了一个“invoice_id”列(这是错误的)以及billing_transaction(这是正确的)。在进一步调试时,我发现了一些有趣的结果,需要一些反馈/建议。

  • Hibernate在billing_transaction中创建了一个列invoice_id,这就是我想要的。

  • Hibernate还会创建另一个具有相同名称的列,即Transaction表中的invoice_id,这不是我想要的。

现在这令人沮丧。即使我在invoice.hbm中提到了表名(Billing_Transaction)并设置了inverse =“true”,但hibernate继续在事务表中创建了一个列invoice_id,即使Billing_Transaction中已有一个。我期待的是,因为我给它的表名hibernate应该采用该名称,并检查billing_transaction是否有invoice_id ...而不是hibernate完全相反。它完全忽略了我提供的表名,并伸向最超级的类,即Transaction。在它发现没有这样的列时,它会创建invoice_id列。结果我在我的表中有两个invoice_id列。在billing_transaction中我想要它,而另一个在事务表中我不希望它。

我找到了导致它的代码。在org.hibernate.mapping.Subclass表中使用下面给出的代码

进行标识
public Property getIdentifierProperty() {
  return getSuperclass().getIdentifierProperty();

}

换句话说, hibernate假设每当有人使用子类时,所有列都应该在类层次结构的顶部根中创建,而不管提供的是哪个表名。有人可以解释一下它背后的推理,如果有办法绕过这个代码,并以某种方式避免在事务表中创建invoice_id ???

1 个答案:

答案 0 :(得分:1)

如果你有

/**
  * MAPPED TO TRANSACTION
  */
public class Transaction {...}

/**
  * MAPPED TO CASH_BASED_TRANSACTION
  */
public class CashBasedTransaction extends Transaction {...}


/**
  * MAPPED TO BILLING_TRANSACTION
  */    
public class BillingTransaction extends CashBasedTransaction {...}

并且Invoice需要一袋BillingTransaction(及其超类CashBasedTransaction)(以及它的超级类Transaction,右)。

这是通过使用外来INVOICE_ID来完成的。是的,只需检索BillingTransaction即可。可以使用其相关的主键检索其超类。因此,CashBasedTransaction和Transaction可能不需要INVOICE_ID。它可以是一个错误???可能是可能不是。 Hibernate Team可能有一些理由生成这个SQL。

请记住:保持代码简单,只需映射客户需求。如果客户需要其他要求,重构(和单元测试)是您最好的朋友。

通知书

  • 继承可以映射为 has -a 而不是是-a

所以,如果你有

/**
  * CashBasedTransaction IS A Transaction
  */
public class CashBasedTransaction extends Transaction {...}

你可以重构为

public class CashBasedTransaction {

   /**
     * CashBasedTransaction HAS A Transaction
     */
    Transaction transaction;

}

<强>更新

避免继承映射的解决方法:删除所有子类并使用 join 映射其继承的属性。只是一个简单的映射(适应您的映射)

<强>父

public class Parent {

    private String parentProperty;
    public String getParentProperty() { return parentProperty; }
    public void setParentProperty(String parentProperty) { this.parentProperty = parentProperty; }

}

儿童

public class Child extends Parent {

    private Integer id;
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

}

<强>发票

public class Invoice {

    private Integer id;
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

    private Collection<Child> childList = new ArrayList<Child>();
    public  Collection<Child> getChildList() { return childList; }
    public void setChildList( Collection<Child> childList) { this.childList = childList; }

}

映射如下所示

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="mapping._3863924.model.domain">
    <class name="Child" table="CHILD">
        <id name="id">
            <generator class="native"/>
        </id>
        <join table="PARENT">
            <key column="CHILD_ID"/>
            <property name="parentProperty"/>
        </join>
    </class>
    <class name="Invoice" table="INVOICE">
        <id name="id">
            <generator class="native"/>
        </id>
        <bag name="childList" table="CHILD">
            <key column="INVOICE_ID"/>
            <one-to-many class="Child"/>
        </bag>
    </class>
</hibernate-mapping>

标准输出显示

create table CHILD (
    id integer generated by default as identity (start with 1),
    INVOICE_ID integer,
    primary key (id)
)

create table INVOICE (
    id integer generated by default as identity (start with 1),
    primary key (id)
)

create table PARENT (
    CHILD_ID integer not null,
    parentProperty varchar(255),
    primary key (CHILD_ID)
)

注意PARENT表不包含 INVOICE_ID。 (这是你想要的,不是吗?)