目标:我想在ImportJob中使用importJobId作为分配表ID的外键,这样,当我们那时具有importJobId时,那么我们只能在ID中分配ID,因为没有Job时就无法进行任何分配。
ImportJob表的复合主键为[ORGID,IMPORTJOBTYPE],正在尝试使用以下方式在hibernate状态下创建外键关系:
<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为了简单起见,我已将其删除。
getter/setter
更新:1 现在的设置方式是,我在一个表中有一个ID列,该列具有对orgId和importJobType的复合键的外键引用,不确定我们是否可以做到这一点,并且具有对另一复合键的单列外键表,但这是我的用例。
更新:2
感谢您提供的精妙细节,这肯定会使我对外键实现有所了解,但是我的最终目标是在两个表之间进行一对一映射,其中表A具有用于标识该表和表B中唯一行的复合键。具有主键,该主键具有对表A的外键引用,因此,如果我们在表A中具有条目,那么同一jobId条目应在表B中,现在我明白了,我们不能在表B中具有单列主键来引用表A中的组合键。
所以基本上我想在表之间建立一对一的映射,其中表A具有使用hibernate的复合主键,表B具有使用hibernate的单列主键,这当然会引起上述错误,所以现在我要在表中创建复合键现在,B还参考表A,因此我将稍后用我的发现验证并更新我的问题,再次感谢您的详细输入。
该错误说明一切,为了引用复合主键,您需要一个复合外键。(复合主键指出,您需要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类(未嵌入)的基本示例。在这里,我们有一个Employee实体,其主键由一个整数id和一个国家(如果在不同的国家/地区,两个Employee可以具有相同的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 } }
注意:
可以完成此操作的另一种方法是通过嵌入式id类:
@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 }
我更喜欢后者,因为它更紧凑并且不包含重复项,但是我又不知道如何使用两者进行比较。