【问题标题】:Hibernate inheritance and multiple tables with same classHibernate继承和具有相同类的多个表
【发布时间】:2011-07-29 12:06:22
【问题描述】:

我对 Hibernate 还是很陌生。在我的情况下,我有一个具体的表,其中包含与许多其他表的连接 ID 的记录——所有表都具有相同的结构。 我想要实现的是得到类似

SELECT * 
  FROM main_records mr, ref1 r1, ref2 r2 
 WHERE r1.id = mr.id_ref1 
   AND r2.id = mr.id_ref2;

主要思想是为所有连接引用重用该类。

SQL

CREATE TABLE main_records
(
  id integer NOT NULL,
  id_ref1 integer NOT NULL,
  id_ref2 integer NOT NULL
)    
CREATE TABLE ref1
(
  id integer NOT NULL,
  value character varying
)
CREATE TABLE ref2
(
  id integer NOT NULL,
  value character varying
)

我已经设置了基础 POJO 类

JAVA 类

public class MainRecord {
  private Integer id;
  private Ref ref1;
  private Ref ref2;
  ...
  // getters and setters
}

public class Ref {
  private Integer id;
  private String value;
  ...
  // getters and setters
}

我的想法是通过以下方式定义 Hibernate 映射:

定义一个抽象超类

<hibernate-mapping package="test">
    <class abstract="true" name="Ref">
        <id name="id" type="java.lang.Integer" column="ID">
            <generator class="native" />
        </id>
        <property name="value" type="java.lang.String" column="VALUE" />
    </class>
</hibernate-mapping>

映射主实体,扩展超类但使用单独的表

<hibernate-mapping package="test">
    <union-subclass name="Ref1" table="REF1" extends="Ref" />
    <union-subclass name="Ref2" table="REF2" extends="Ref" />

    <class name="MainRecord" table="MAIN_RECORDS">
        <id name="id" column="ID" type="java.lang.Integer" />
        <many-to-one name="ref1" class="Ref1" column="ID_REF1" fetch="join" unique="true" />
        <many-to-one name="ref2" class="Ref2" column="ID_REF2" fetch="join" unique="true" />
     </union-subclass>
  </class>
</hibernate-mapping>

我确实在配置中手动包含映射文件,加载似乎还可以,但随后出现错误,没有任何详细说明:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.cxf.transport.servlet.ServletTransportFactory' defined in class path resource [META-INF/cxf/cxf-servlet.xml]: Error setting property values; 
nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (2) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'bus' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring/database.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
PropertyAccessException 2: org.springframework.beans.MethodInvocationException: Property 'transportIds' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/spring/database.xml]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.entity.PojoEntityTuplizer]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1361)

该系统混合了 Spring 2.5、Hibernate 3.2、Cxf 2.3.4、Javassist 3.11,

我的问题是:

(a) 这是正确的方法吗?

(b) 我一介绍就出错了

<union-subclass name="Ref1" table="REF1" extends="Ref" />
<union-subclass name="Ref2" table="REF2" extends="Ref" />

所以我想这不是最好的方法吗?

(c) 可以用注解写吗?如果不为它们实际创建 POJO 类,我无法理解如何定义 Ref1 和 Ref2 类。 (d) 我可以使用 1 级以上的继承吗?例如,我想为我所有的具体表使用一个抽象超类,它涵盖了它们共有的审计字段? 假设类 Ref 在 Java 和 Hibernate 映射中扩展了一个抽象的 AuditTable 类。

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    如果我正确理解了这个问题,您有一个主记录表,其中包含多个外键到许多彼此具有相同列的表?

    (a) 这是正确的方法吗?

    不,您正在尝试通过使用 Table per class 策略来进行继承,我认为这里不适合使用,因为您对每个类型对象都有一个引用(在您的情况下是字段 Ref1 和字段 Ref2)。 适合使用继承的用例是,如果您在 MasterRecord 对象中与 Ref 有一个多态关联

    与 Ref 的多态关联示例

    public class MasterRecord{
    
        Long id;
    
        Ref anyObjectRef1OrRef2; <-- Polymorphic association
    }
    

    关联 anyObjectRef1OrRef2 将与 MasterRecord 映射中的 &lt;any ... /&gt; 元素进行映射。它需要两列,第一列是 className,另一列是 foreignKey

    (b)我一介绍&lt;union-subclass就会出现错误...所以我想这不是最好的方法吗?

    你应该做的是从超类继承属性(这个超类没有特定的表)

    (C)可以用注解写吗?

    是的,使用@MappedSuperclass 注解。
    Hibernate 参考实现(使用注解)

    (C-a)如果没有,我无法理解如何定义 Ref1 和 Ref2 类 实际上为他们创建了一个 POJO 类?

    不创建 Ref1 和 Ref2 pojo 类就无法完成。

    (d)我可以使用多于 1 级的继承吗?例如,我想 为我所有的具体表使用一个抽象超类,涵盖 他们都有共同的审计领域?假设类 Ref 扩展 一个抽象的 AuditTable

    带注释的示例

    基类引用.java

    @MappedSuperclass
    public abstract class Ref {
    
        String value;
    
    }
    

    类 Ref1.java

    @Entity
    public class Ref1 extends Ref {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        Long id;
    }
    

    类 Ref2.java

    @Entity
    public class Ref2 extends Ref {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        Long id;
    }
    

    类 MainRecord.java

    @Entity
    public class MainRecord {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        Long id;
    
        @ManyToOne(cascade=CascadeType.ALL, targetEntity=Ref1.class)    
        Ref ref1;
    
        @ManyToOne(cascade=CascadeType.ALL, targetEntity=Ref2.class)
        Ref ref2;
    }
    

    如下创建并保存实体

    ..begin transaction
    
    MainRecord mainRecord = new MainRecord();
    
    Ref1 ref1 = new Ref1();
    ref1.setValue("tettset");
    
    Ref2 ref2 = new Ref2();
    ref2.setValue("tettset");
    
    mainRecord.setRef2(ref2);
    mainRecord.setRef1(ref1);
    
    entitymanager.persist(mainRecord);
    
    ..commit transaction.
    

    要使用休眠映射文件,您需要使用下面的映射删除所有注释。

    并不是说在使用映射文件时,抽象Ref.java超类没有被映射,超类的所有属性都在每个子映射文件中。

    <class name="MainRecord" table="MAIN_RECORDS">
            <id name="id" column="uid" type="long">
                    <generator class="identity"/>
            </id>
            <many-to-one name="ref1" class="Ref1" column="ID_REF1" fetch="join" unique="true"  cascade="all" />        
            <many-to-one name="ref2" class="Ref2" column="ID_REF2" fetch="join" unique="true"  cascade="all" />     
    
    </class>
    
    <class name="Ref1" table="REF1">
            <id name="id" column="uid" type="long">
                    <generator class="identity"/>
            </id>
            <property name="value" type="java.lang.String" column="VALUE" />
    </class>
    
    <class name="Ref2" table="REF2">
            <id name="id" column="uid" type="long">
                    <generator class="identity"/>
            </id>
            <property name="value" type="java.lang.String" column="VALUE" />
    </class>
    

    【讨论】:

    【解决方案2】:

    只有一个问题....如果您使用同一个类来访问多个不同的表... 您保存的新实例将存储到哪个表? Hibernate 怎么知道?

    AFAIK,这是不可能的。

    【讨论】:

    • 不应该通过 Ref1 和 Ref2 类解决吗?他们定义了正确的表格。类 Ref 仅用于字段映射。
    【解决方案3】:

    诸如此类的快捷方式,即使它们可以正常工作,也常常会导致继承您的代码的开发人员最终感到困惑。在问“可以做到”之前,先问“应该做到”。

    想一想:是在开发时通过创建更少的类并使用一些晦涩的功能来提高“效率”,还是只制作单独的类并让事情更容易理解更好。此外,您可能会陷入困境,因为此特定功能可能会导致意想不到的后果,即经典的“为什么要这样做”的情况。

    我继承了许多应用程序,前几代开发人员希望以“酷”或“优雅”的方式做事,结果导致比让事情具体和简单更混乱。

    只需创建单独的类。

    【讨论】:

      猜你喜欢
      • 2012-01-10
      • 1970-01-01
      • 2015-07-18
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多