Как сопоставить коллекцию объектов базового типа в производном типе того же дерева наследования? - PullRequest
3 голосов
/ 22 июля 2010

Фон

Модель объекта такова, что:

  • «Компонент» - это примитив, он не может состоять из более мелких частей.
  • «Часть» является составной. Он может состоять из «Компонентов», других «Частей» или любой их комбинации.

Я смоделировал это, имея часть, унаследованную от Компонента, и затем получив часть, имеющую коллекцию объектов Компонента, из которых она состоит.

Предположения

  • Java 1.6
  • Hibernate 3.5.1-Final
  • Отображение файлов HBM (против аннотаций)

Задача

Когда я пытаюсь создать объект Part, я получаю нарушение ограничения целостности:

14:29:24,121  WARN JDBCExceptionReporter:100 - SQL Error: -177, SQLState: 23503
14:29:24,126 ERROR JDBCExceptionReporter:101 - integrity constraint violation: foreign key no parent; FK24013CDD89C7219B table: COMPONENT

Глядя на схему, которую hbm2ddl генерирует ниже, кажется, что она создает невозможную ситуацию.

Наследование FK

Таблица Part ссылается на таблицу компонентов через FK для PartID-> ComponentID из-за таблицы на подкласс, что и следовало ожидать.

alter table Part add constraint FK25D813296E407B foreign key (PartID) references Component;

Коллекция ФК

Он также создает отношение из таблицы Component к таблице Part через FK, чтобы ComponentID представлял коллекцию. Однако то, как это было сделано, было немного неисследовано.

alter table Component add constraint FK24013CDD89C7219B foreign key (ComponentID) references Part;

Я бы ожидал, что он сгенерирует следующее, чтобы представлять коллекцию:

alter table Component add constraint FK24013CDD89C7219B foreign key (PartID) references Part;

или в более общем смысле

alter table <Name of class in the collection> add constraint FK123 foreign key (id of the class object which owns the collection) references (Name of the class that holds the collection)

Я вижу, как он может выбрать ComponentID, поскольку на самом деле PartID FK'd для ComponentID из-за наследования. Однако, как результат, я считаю, что это создало невозможную ситуацию, когда таблица компонентов теперь имеет требование FK к себе, которое никогда не может быть удовлетворено.

Вопрос

Как отобразить коллекцию объектов базового типа в производный тип того же дерева наследования?

Код

Java POJOs

package manufacturing;

public class Component {
    private long _id;
    private String _name;
    private float _price;

    // Hibernate requires all persistent classes to have a default constructor, even if it is private.
    @SuppressWarnings("unused")
    protected Component() {
        this(-1L, "", 0.0f);
    }

    public Component(String name) {
        this(-1L, name, 0.0f);
    }

    public Component(long id, String name, float price) {
        setID(id);
        setName(name);
        setPrice(price);
    }

    public long getID() {
        return _id;
    }

    // Required method for Hibernate
    protected void setID(long id) {
        _id = id;
    }


    public String getName() {
        return _name;
    }

    public void setName(String name) {
       _name = name;
    }

    public float getPrice() {
        return _price;
    }

    public void setPrice(float price) {
        _price = price;
    }
}


package manufacturing;

import java.util.Collection;
import java.util.HashSet;

public class Part extends Component {
    private Collection<Component> _subcomponents;

    // Hibernate requires all persistent classes to have a default constructor, even if it is private.
    @SuppressWarnings("unused")
    private Part() {
        this(-1L, "", 0.0f, new HashSet<Component>());
    }

    public Part(String name) {
        this(-1L, name, 0.0f, new HashSet<Component>());
    }

    public Part(long id, String name, float price, Collection<Component> subcomponents) {
        super(id, name, price);
        setSubcomponents(subcomponents);
    }

    public Collection<Component> getSubcomponents() {
        return _subcomponents;
    }

    public void setSubcomponents(Collection<Component> subcomponents) {
        _subcomponents = subcomponents;
    }

}

Hibernate Mapping

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
                        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="manufacturing">
 <class name="Component" table="Component">
  <id name="ID" column="ComponentID">
   <generator class="native" />
  </id>
  <property name="name" column="Name" />
  <property name="price" column="Price" />        

  <joined-subclass name="Part" table="Part">
   <key column="PartID" />
   <!-- A part has has [0..n] subcomponents -->
   <set name="Subcomponents" lazy="true" cascade="all" inverse="true">
    <key column="ComponentID" />
    <one-to-many class="Component" />
   </set>

  </joined-subclass>
 </class>
</hibernate-mapping>

HBM2DDL Схема

create table Component (ComponentID bigint generated by default as identity (start with 1), Name varchar(255), Price float, primary key (ComponentID));
create table Part (PartID bigint not null, primary key (PartID));
alter table Component add constraint FK24013CDD89C7219B foreign key (ComponentID) references Part;
alter table Part add constraint FK25D813296E407B foreign key (PartID) references Component;

Reckvent Stack Trace

14:28:15,001 DEBUG SessionImpl:257 - opened session at timestamp: 12798232950
14:28:47,299 DEBUG JDBCTransaction:82 - begin
14:28:47,313 DEBUG ConnectionManager:444 - opening JDBC connection
14:28:47,333 DEBUG JDBCTransaction:87 - current autocommit status: false
14:29:23,929 DEBUG AbstractSaveEventListener:320 - executing identity-insert immediately
14:29:23,941 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
14:29:23,948 DEBUG SQL:111 - insert into Component (ComponentID, Name, Price) values (null, ?, ?)
Hibernate: insert into Component (ComponentID, Name, Price) values (null, ?, ?)
14:29:23,966 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
14:29:23,991 DEBUG JDBCExceptionReporter:92 - could not insert: [manufacturing.Part] [insert into Component (ComponentID, Name, Price) values (null, ?, ?)]
java.sql.SQLException: integrity constraint violation: foreign key no parent; FK24013CDD89C7219B table: COMPONENT
 at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
 at org.hsqldb.jdbc.JDBCPreparedStatement.fetchResult(Unknown Source)
 at org.hsqldb.jdbc.JDBCPreparedStatement.executeUpdate(Unknown Source)
 at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:94)
 at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57)
 at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
 at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2836)
 at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
 at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
 at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
 at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
 at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
 at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
 at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
 at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
 at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
 at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
 at DAO.GenericHibernateDAO.create(GenericHibernateDAO.java:91)
 at DAO.PartDAOHibernateTest.testCreatePartWithNoSubcomponents(PartDAOHibernateTest.java:69)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
 at java.lang.reflect.Method.invoke(Unknown Source)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
 at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
 at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
 at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
14:29:24,121  WARN JDBCExceptionReporter:100 - SQL Error: -177, SQLState: 23503
14:29:24,126 ERROR JDBCExceptionReporter:101 - integrity constraint violation: foreign key no parent; FK24013CDD89C7219B table: COMPONENT

1 Ответ

1 голос
/ 23 июля 2010

Здесь есть две проблемы, обе с файлом сопоставления.

Компонент является примитивом, он не может быть далее разделен и поэтому не имеет подкомпонентов.Часть является составной и, таким образом, может иметь несколько подкомпонентов, которые являются либо Компонентами, Частями, либо некоторой их комбинацией.Следовательно, Компонент является базовым классом, а Part является производным классом, который добавляет коллекцию Компонентов.В этой связи объекты-Компоненты - это то, что собрано , а объект-часть - это то, что удерживает эту коллекцию.

Проблема № 1 - Ключ установки

Набор с именем «Subcomponents» правильно размещен в классе Part, однако ключевой столбец неправильно указывает на ComponentID, когда он должен указывать на PartID.Согласно Справочной документации по Hibernate Раздел 6.2.1.Внешние ключи коллекции:

Экземпляры коллекции различаются в базе данных по внешнему ключу объекта, которому принадлежит коллекция.

Так что здесь, поскольку Часть - это то, что владеетколлекция, PartID должен использоваться для его ввода.

Проблема № 2 - Инверсия

С Hibernate - Внутреннее объяснение инверсии = true

Инверсия определяет, какая сторона отвечает за поддержание ассоциации.Сторона, имеющая inverse = "false" (значение по умолчанию) несет эту ответственность и создаст соответствующий запрос SQL. Изменения, внесенные в ассоциацию на стороне inverse = "true" , не сохраняются в БД.

Так что здесь, поскольку это не 'При двунаправленном сопоставлении Партия отвечает за сбор и должна быть помечена inverse = "false" (или полностью исключить этот атрибут, поскольку по умолчанию установлено значение false).Если вы не пометите его как ложное и вместо этого отметите как истинное, в итоге вы получите пустое значение PartID в таблице компонентов для подкомпонентов и, возможно, дублирующих компонентов.Другими словами, он сохранит все объекты в коллекции, но не сохранит информацию об отношениях, которая сообщает Hibernate, какие компоненты принадлежат к какой коллекции Part.

Код

Правильный HibernateОтображение

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
                        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="manufacturing">
    <class name="Component" table="Component">
        <id name="ID" column="ComponentID">
            <generator class="native" />
        </id>
        <property name="name" column="Name" />
        <property name="price" column="Price" />        

        <joined-subclass name="Part" table="Part">
            <key column="PartID" />
            <!-- A part has has [0..n] sub-components -->
            <set name="Subcomponents" lazy="true" cascade="all" inverse="false">
                <key column="PartID" />
                <one-to-many class="Component" />
            </set>

        </joined-subclass>
    </class>
</hibernate-mapping>

Полученная схема:

create table Component (ComponentID bigint generated by default as identity (start with 1), Name varchar(255), Price float, PartID bigint, primary key (ComponentID));
create table Part (PartID bigint not null, primary key (PartID));
alter table Component add constraint FK24013CDD89368311 foreign key (PartID) references Part;
alter table Part add constraint FK25D813296E407B foreign key (PartID) references Component;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...