Проблема: таблица для каждого подкласса с использованием дискриминатора - PullRequest
1 голос
/ 05 октября 2010

Краткое изложение проблемы

Hibernate предполагает, что всякий раз, когда кто-то использует подкласс, все столбцы должны быть созданы в верхнем корне иерархии классов, независимо от того, какое имя таблицы было предоставлено. Может кто-то объяснить, почему за этим стоит и если есть способ обойти этот код и как-то избежать создания invoice_id в таблице транзакций ???

Подробнее

У меня очень глубокая иерархия классов в моей доменной модели, и я хочу использовать тег дискриминатора для поддержки наследования.

Изначально я отобразил свои классы с помощью join-subclass, однако используемые таблицы содержат сотни тысяч записей, и это оказывается узким местом в производительности. По сути, я хочу сгладить структуру таблицы, не касаясь области, и поэтому мы перешли к тегу дискриминатора.

Мои отображения в режиме гибернации выглядят так (удалили дополнительный столбец для простоты)

Транзакция Отображение

<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 создает столбец invoice_id в billing_transaction, что я и хочу.

  • Hibernate также создает еще один столбец с тем же именем, то есть invoice_id в таблице транзакций, что не то, что я хочу.

Теперь это расстраивает. Несмотря на то, что я упомянул имя таблицы (Billing_Transaction) в invoice.hbm и установил inverse = "true", все же hibernate идет вперед и создает столбец invoice_id в таблице транзакций, хотя в Billing_Transaction он уже есть. Я ожидал, что, поскольку я даю ему имя таблицы, hibernate должен взять это имя и проверить, имеет ли billing_transaction invoice_id или нет ... Вместо этого hibernate делает в точности наоборот. Он полностью игнорирует имя таблицы, которое я предоставил, и обращается к самому суперклассу, то есть транзакции. Там он создает столбец invoice_id, когда обнаруживает, что такого столбца не существует. В результате у меня есть два столбца invoice_id в моих таблицах. Один в billing_transaction, где я хочу, и другой в таблице транзакций, где я не хочу.

Я нашел код, который его вызывает. В org.hibernate.mapping.Subclass таблица идентифицируется с помощью приведенного ниже кода

public Property getIdentifierProperty() {
  return getSuperclass().getIdentifierProperty();

}

Другими словами hibernate предполагает, что всякий раз, когда кто-то использует подкласс, все столбцы должны создаваться в верхнем корне иерархии классов независимо от того, какое имя таблицы было предоставлено. Может кто-нибудь объяснить, почему за этим, и если есть способ обойти этот код и как-то избежать создания invoice_id в таблице транзакций ???

1 Ответ

1 голос
/ 06 октября 2010

Если у вас есть

/**
  * 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 {...}

И для счета-фактуры требуется пакет BillingTransaction (и его суперкласс CashBasedTransaction) (и его наследующая транзакция суперкласса, верно).

Это достигается использованием стороннего INVOICE_ID.Да, имеет смысл просто извлечь BillingTransaction.Его суперкласс можно получить с помощью соответствующего первичного ключа.Поэтому, возможно, CashBasedTransaction и Transaction не нуждаются в INVOICE_ID.Это может быть ошибка ???может да может нет.Hibernate Team может иметь некоторые причины для генерации этого SQL.

имейте в виду : сохраняйте свой код простым и просто отображайте потребности клиентов.Если клиентам нужны другие требования, рефакторинг (и модульное тестирование) - ваш лучший друг.

Советы

  • Наследование можно сопоставить как has-a вместо is-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.(Это то, что вы хотите, не так ли?)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...