7

目标:我想将 ImportJob 中的 importJobId 作为分配表 id 的外键,这样当我们有 importJobId 时,只有我们可以在分配中有 id,因为没有 Job 就不能有任何分配。

ImportJob 表的复合主键为 [ORGID,IMPORTJOBTYPE] 并尝试使用在休眠中创建外键关系

  <id name="id"
        column="ID">
        <generator class="native"/>
    </id>
    <many-to-one name="importjobid"
                 class="com.delta.pdo.admin.ImportJob"
                 cascade="save-update"/>

在 Allocation.hbm.xml 中无法正常工作,并且收到错误消息:

Foreign key (FKB29B5F7366007086:ALLOCATIONS [importjobid])) 
must have same number of columns as the 
referenced primary key (IMPORTJOBMANAGMENT [ORGID,IMPORTJOBTYPE])

这是我的 ImportJob.hbm.xml 文件

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.delta.pdo.admin.ImportJob" table="IMPORTJOB" lazy="false">
        <!-- we don't cache this since the commissions code is too screwed up to work with -->
        <composite-id>
            <key-property name="orgId" type="long" column="ORGID"/>
            <key-property name="importJobType" type="java.lang.String" column="IMPORTJOBTYPE"/>
        </composite-id>

        <!-- Make sure importjobid is not-null='true '-->
        <property name="importjobid" type="long" column="IMPORTJOBID" />
        <property name="allocations" type="boolean" column="ALLOCATIONS" />
    </class>
</hibernate-mapping>

以下是供参考的 bean 类:

public class AllocationBean extends WorkbenchBeanBase
{
    private static final Logger log = Logger.getLogger(AllocationBean.class);
    private Float allocations;
    private String importJobType;
    private long id;
    private long orgId;
}

public class ImportJobManagment implements Serializable
{
    private long importjobid;
    private long orgId;
    private String importJobType;
    private boolean allocations = false;
}

getter/setter为了简单起见,我已经删除了。

更新:1 现在的设置方式,我在一个表中有 id 列,该表具有对 orgId 和 importJobType 的复合键的外键引用,不确定我们是否可以这样做并且将单列外键键控到另一个的复合键表,但这是我的用例。

更新:2

感谢您提供令人敬畏的细节,这肯定会增强我对外键实现的了解,但我的最终目标是在两个表之间进行一对一映射,其中表 A 具有用于识别该表和表 B 中唯一行的复合键,我想有主键,它有对表 A 的外键引用,这样如果我们在表 A 中有条目,那么相同的 jobId 条目应该在表 B 中,现在我明白你的意思是我们不能在表 B 中有单列主键来引用表 A 中的复合键。

所以基本上我希望在表 A 具有复合主键和表 B 具有使用休眠的单列主键的表之间进行一对一映射,这当然会得到提到的错误,所以现在我要在表中创建复合键B 现在也对表 A 进行外键引用,稍后我将用我的发现验证和更新我的问题,再次感谢您的详细输入。

4

2 回答 2

14

该错误不言自明,为了引用复合主键,您需要一个复合外键。(复合主键声明您需要 2 个字段的唯一组合来创建键 - 那么您不可能仅使用 1 列引用唯一键。)

至于如何通过使用 xml 映射文件来实现这一点,我不确定,现在大多数人都使用注释..

至于您的 Java 类,我假设 ImportJobManagement 拥有一个 ImportJob,因此该类不应引用 id,而是对象本身,如下所示:

public class ImportJobManagment implements Serializable {
    private ImportJob importJob;
    ...
}

Java 类应该只引用另一个类,而不是复合键的成员——这取决于从复合键映射到 Java 成员变量的映射。

更新答案:

简短的回答是不,你不能。外键的工作方式是让您引用表中的特定行。而且,为了确保引用特定的行,您需要一个标识,该标识仅描述一行,而没有其他行。在 SQL 中,有一个实现这一点的结构,即唯一键。通过声明一列(或列的组合)是唯一的,您知道它/它们的组合值将是唯一的,整个表中最多有 1 行具有此值,否则将违反约束.

因此,外键指的是单个唯一受约束的列,或跨多个列的复合唯一键。由于所有表都已经有一个唯一键,即主键(始终是唯一的),因此通常将其用于外键引用,但任何 unqiue 列都可以使用。

最简单的情况是当我们想要引用具有单列唯一键的表时。两个简单的表 A,它包含一个单列“id”,B,它包含一个“id”列,还有另一个列,a_id,它具有 A 的“id”列的外键。这个的一个实例情况可能是这样的:

A:
| id | 
|----|
|  1 |
|  2 |
|  3 |

B:
| id | a_id |
| 2  |  3   |
| 3  |  1   |

这里,B中的每一行引用A中的一行。它是直接引用,表B中a_id中的值直接对应于A''id'列中的值。所以 id 为 2 的 B 引用了 id 为 3 的 A,依此类推。

现在让我们看看如何使用复合唯一键引用表。让我们保留我们的示例,但现在 A 有另一列 'sec_id',它与 'id' 一起组成一个复合主键。

A:
| id | sec_id |
|----|--------|
| 1  |   3    |
| 3  |   1    |
| 3  |   7    |

B:
| id | a_id |
|----|------|
| 2  |  3   |

在这种情况下,我们在 B 中遇到了问题。由于外键必须引用它所引用的表中的单行,这显然是行不通的。值“3”代表 A 中的哪一行?第一行的 sec_id?第二个或第三个中的 id (但在这种情况下,是哪一个?)?答案当然不是,B 中没有足够的信息来引用 A 中的单行,所以 SQL 就没有它。因此不允许添加这样的外键。

为了让 B 引用 A,它需要同时引用 A 的 'id' 列和 A 的 'sec_id' 列,因为 A 中的单行由其唯一组合 ('id', 'sec_id' ) 对。所以 B 看起来像这样:

| id | a_id | a_sec_id |
|----|------|----------|
| 1  |  1   |     3    |
| 2  |  3   |     1    |
| 3  |  3   |     7    |

现在,B 拥有足够的信息来引用 A 中的单行,并且如数据所示,确实如此。

再次更新:

我目前正在阅读 JPA 认证,并且已经阅读了关于复合键映射的章节。为了映射复合主键,您需要一个映射键属性的主键类。有两种方法,一种是键属性也必须映射到实体本身,另一种是用作嵌入键。

我将提供代码示例,它们很能说明问题(它使用注释,你真的应该这样做。)

第一个示例是基本示例,具有常规的 id-class(未嵌入)。在这里,我们有一个 Employee 实体,其中主键由一个整数 id 和一个国家/地区组成(如果在不同的国家/地区,两个员工可以具有相同的 id)。

@Entity
@IdClass(EmployeeId.class)
public class Employee {
    @Id private String country
    @Id
    @Column(name = "EMP_ID")
    private int id;
    private String name;
    ...
}

public class EmployeeId implements Serializable {
    private String country;
    private int id;

    public EmployeeId() { }
    public EmployeeId(final String country, final int id) {
        this.country = country;
        this.id = id;
    }

    //getters for the properties

    public boolean equals(final Object other) {
    //must be implemented
    }

    public int hashCode() {
    //must be implemented
    }
}

笔记:

  • 类上的@IdClass-annotation。
  • id-class 的两个属性也必须在实体中定义
  • id-class 必须实现 equals 和 hashcode

另一种方法是通过嵌入的 id-class:

@Entity
public class Employee {
    @EmbeddedId
    private EmployeeId id;
    private String name;

    public Employee(final String country, final int id) {
        this.id = new EmployeeId(country, id);
    }

    public String getCountry() {
        return id.getCountry();
    }
}   

@Embeddable
public class EmployeeId {
   private String country;
   @Column(name = "EMP_ID")
   private int id;

   //constructor + getters + equals +hashCode
}

笔记:

  • 无需在实体中定义 id-attributes
  • 要从实体中获取 id-class 的属性,您需要从 id-class 中获取它们

我更喜欢后者,因为它更紧凑并且不包含重复,但是我不知道如何使用这两个比较..

于 2013-01-24T22:36:07.033 回答
-1

导入作业.hbm.xml

使用这个添加insert = false到 importjobid 如下所示:

<property name="importjobid" type="long" 
    column="importjobid" type="long" insert="false"/>
于 2013-11-27T04:35:22.370 回答