问题摘要
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之后展平表结构。换句话说,我想在数据库中只有三个表
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 ???
答案 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。
请记住:保持代码简单,只需映射客户需求。如果客户需要其他要求,重构(和单元测试)是您最好的朋友。
通知书
所以,如果你有
/**
* 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。 (这是你想要的,不是吗?)