【问题标题】:Hibernate, mapping inherited fieldsHibernate,映射继承的字段
【发布时间】:2024-09-02 19:20:02
【问题描述】:

假设我有以下类层次结构:

class Person {
    String name;
    int age;
}
class DatabasePerson extends Person {
    int databaseId;
}

现在我想映射DatabasePerson,请注意我不想告诉hibernate存在一个Person类,hibernate应该只知道DatabasePerson类,而xml或注解映射。是否可以使用采用上述条件的休眠映射年龄和姓名字段?换句话说,我想用hibernate映射DatabasePerson类,而hibernate不应该知道Person类。在xml中我会这样(伪代码):

<hibernate class="DatabasePerson">
     <field name="id"/>
     <field name="name"/>
     <field name="age"/>
</hibernate>

这样做的原因是为了保持单一责任原则。我不想将 databaseId 字段放入 Person 类,因为 person 类不应该知道它是持久的。我不想在 Person 类中包含任何休眠注释,因为我有纯实体逻辑,我不想在那里导入任何与数据库相关的东西,比如休眠。我想在DatabasePerson类中做的所有映射,这是我想放置额外的databaseId字段的地方,写hibernate注解(或者可能是xml,我现在不知道,我想推迟这个决定)。换句话说,我们希望将数据库相关的东西和我们的应用程序逻辑保存在单独的类中。

编辑: 我可以使用这样的东西吗?:

<class name="Person" table="PERSON" discriminator-value="P">


    <discriminator column="DISCRIMINATOR" type="string" />

    <property name="name" />
    <property name="age"  />

    <subclass name="DatabasePerson" extends="Person" >
        <id name="databaseId" column="ID">
            <generator class="native" />
        </id>

    </subclass>
</class>

请注意,数据库 ID 在此 xml 中的 DatabasePerson 范围内。

编辑: 此 xml 映射是否与注释“映射的超类”相对应?我想我更喜欢使用 xml 而不是注释,所以我的问题是如何在 xml 中使用 @MappedSuperclass:

<class name="Person" table="PERSON" discriminator-value="P">
    <id name="databaseId" column="PERSON_ID">
        <generator class="native" />
    </id>

    <discriminator column="DISCRIMINATOR" type="string" />

    <subclass name="DatabasePerson" extends="Person" discriminator-value="E">
            <property name="name" column="name" />
            <property name="age" type="int" column="age" />
    </subclass>
</class>

【问题讨论】:

    标签: java hibernate


    【解决方案1】:

    我认为您可以使用 MappedSupperclass 注释或单表继承策略来仅使用数据库中的一个表。两种情况下,JPA/Hibernate 都知道 Person 类,但它不会为此类创建表。 (当然你可以用XML调整代替注解,但我只用过注解。)

    @MappedSuperclass 注解。

    如果您只想使用一个包含两个 java 类的所有列的数据库表 (DatabasePerson),则可以使用以下注释:

    @MappedSuperclass
    class Person {
        String name;
        int age;
    }
    
    @Entity
    class DatabasePerson extends Person {
        int databaseId;
    }
    

    我认为在这种情况下最好将 Person 类更改为抽象类。


    InheritanceType.SINGLE_TABLE

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(
        name="yourtype",
        discriminatorType=DiscriminatorType.STRING
    )
    class DatabasePerson{
        int databaseId;
    }
    
    
    @Entity
    @DiscriminatorValue("P")
    class Person extends DatabasePerson{
            String name;
            int age;
        }
    

    此策略使用鉴别器列并仅保留一个表。

    【讨论】:

      【解决方案2】:

      试试看

      <hibernate-mapping package="Yourpackage">
      
          <class name="Person" table="PERSON" >
              <id name="databaseId" column="ID">
                  <generator class="native" />
              </id>
      
              <discriminator column="DISCRIMINATOR" type="string" />
      
              <property name="name" />
              <property name="age"  />
      
              <subclass name="DatabasePerson" extends="Person" >
                      <property name="databaseId" column="database_Id" />
      
              </subclass>
          </class>
      </hibernate-mapping>
      

      这里可能有同样的例子

      http://viralpatel.net/blogs/hibernate-inheritence-table-per-hierarchy-mapping/

      【讨论】:

      • .. ”代码不应该在 DatabasePerson 范围内吗?请注意,我不想在 Person 类中存储任何数据库 id,只在 DatabasePerson 中存储,这就是我创建子类的原因。
      【解决方案3】:

      Hibernate XML 配置完美地处理了它,我在我从事的一个项目中广泛使用了它。我的场景如下,听起来可能不同,但和你的一样。

      class Entity {
          private int _id;
          public int getId() { return _id; }
          public void setId(int id) { _id = id; }
      }
      
      class User extends Entity {
          private String _email;
          public String getEmail() { return _email; }
          public void setEmail(String email) { _email = email; }
      }
      

      我的意图是让 User(和我所有其他实体)继承 id,因为您试图让 DatabasePerson 继承 Person.name 和 age。

      然后,在为用户配置映射时,我编写了以下 XML 文件。

      <hibernate-mapping>
          <class name="User" table="User">
              <id name="id" type="int" column="id">
                  <generator class="identity"/>
              </id>
              <property name="email" type="string">
                   <column name="email" sql-type="varchar(255)"/>
              </property>
      
              .....
          </class>
      </hibernate-mapping>
      

      如您所见,上述 XML 文件中没有对 Entity 的任何单一引用,在您的情况下转换为:您的 XML 文件仅处理 DatabasePerson,因为 Person 根本不存在; Hibernate 将使用 Java 自省从实体中检索 id,作为您案例的 Person.name。

      我唯一不确定的是,您的 Person.name 和 Person.age 字段是否可以保留为受保护的,或者是否应该公开。无论哪种方式,您都可以将它们保留为受保护并(最终)提供方法 DatabasePerson.getName() 和 DatabasePerson.setName()(无需在 Person 级别提供这些 getter/setter)。

      作为旁注,在其他答案中,建议您使用休眠“子类”或“MappedSuperclass”。这些不是必需的,在您的情况下,它们的使用甚至在语义上是错误的:它们应该在您有一个扩展另一个数据库实体的数据库实体时使用,但在您的情况下,DatabasePerson 是一个扩展 POJO 类(人)的数据库实体。

      【讨论】:

      • 对此完全确定吗?我问是因为我们现在正在编写应用程序逻辑(我们现在不关心数据库)。为我们的应用程序编写整个逻辑大约需要 6 个月。完成后,我们想关闭这些纯逻辑类进行修改,因此我们想用数据库类扩展我们的逻辑类。如果我们仅仅因为 hibernate 的需求而不得不重构我们的实体类,那将是一个巨大的痛苦,比如提供 getter、setter 来访问字段,添加新的字段,比如 databaseId。
      • @friko 我已经编辑了我的答案以更清晰。希望它是可以理解的。
      最近更新 更多