代码之家  ›  专栏  ›  技术社区  ›  Zach Burlingame

如何在同一继承树的派生类型中映射基类型对象的集合?

  •  3
  • Zach Burlingame  · 技术社区  · 16 年前

    对象模型如下:

    • “零件”是一种复合材料。它可以由“组件”、其他“部件”或两者的任意组合组成。

    我通过让一个部件从组件继承,然后让该部件拥有一个由其组成的组件对象集合来对此进行建模。

    • 休眠3.5.1-最终
    • HBM文件映射(vs注释)

    问题

    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引用PartID的组件表->由于每个子类的表而导致的组件,这是预期的。

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

    FK系列

    它还通过FK为表示集合的ComponentID生成从Component表到Part表的关系。然而,它是如何做到这一点是稍不接受。

    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)
    

    问题

    代码

    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;
        }
    
    }
    

    休眠映射

    <?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;
    

    相关堆栈跟踪

    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 回复  |  直到 16 年前
        1
  •  1
  •   Zach Burlingame    16 年前

    这里有两个问题,都与映射文件有关。

    组件是一个原语,不能进一步划分,因此没有子组件。零件是一个复合材料,因此可以有多个子组件,这些子组件可以是组件、零件,也可以是它们的组合。因此,组件是基类,而部件是添加组件集合的派生类。在这种关系中,组件对象就是所要处理的对象 部分对象是什么 那个收藏。

    问题1-设置键

    名为“Subcomponents”的集合被正确地放置在Part类中,但是key列在应该指向PartID时错误地指向了ComponentID。根据 Hibernate Reference Documentation 第6.2.1节。集合外键:

    集合实例是可区分的 拥有集合的实体。

    因此,在这里,由于Part是集合的所有者,因此应该使用PartID对其进行键控。

    Hibernate - Inside explanation of inverse=true

    反向定义哪一边是 对协会负责 (默认值)有此责任和意愿 创建适当的SQL查询。 对协会所做的更改

    因此,在这里,由于这不是双向关联,因此该部件负责收集并应进行标记 inverse=“假”

    代码

    正确的休眠映射

    <?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;