Hibernate继承和具有相同类的多个表

Posted

技术标签:

【中文标题】Hibernate继承和具有相同类的多个表【英文标题】:Hibernate inheritance and multiple tables with same class 【发布时间】: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 类。

【问题讨论】:

【参考方案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 参考实现(使用注解)

Mapping inheritance 见第 2.2.4.4 节。从超类继承属性

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

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

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

Section 2.2.4.4. Inherit properties from superclasses

带注释的示例

基类引用.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>

【讨论】:

感谢您的示例,进入这个方向机器人遇到了新的麻烦。更多细节在这里***.com/questions/7105832/…【参考方案2】:

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

AFAIK,这是不可能的。

【讨论】:

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

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

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

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

只需创建单独的类。

【讨论】:

以上是关于Hibernate继承和具有相同类的多个表的主要内容,如果未能解决你的问题,请参考以下文章

java 当一个接口被多个类继承时 如何知道是调用的哪个类的实现

在 Hibernate 中映射 2 个具有相同结构的表

继承和多态

Hibernate:如何从具有多个类的查询中获取结果

Java中 继承的概述与定义实例

Hibernate - JPA 在不同的情况下生成具有相同名称的重复表