Hibernate问题:外键必须与引用的主键具有相同数量的列

Rac*_*hel 5 java sql hibernate foreign-keys composite-key

目标:我想将ImportJob中的importJobId作为分配表的id的外键,这样当我们拥有importJobId然后只有我们可以在分配中拥有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"/>
Run Code Online (Sandbox Code Playgroud)

在Allocation.hbm.xml中没有运行并收到错误消息:

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

这是我的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>
Run Code Online (Sandbox Code Playgroud)

以下是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;
}
Run Code Online (Sandbox Code Playgroud)

getter/setter为简单起见,我删除了.

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

更新:2

感谢非常棒的细节,这肯定会增强我对外键实现的了解,但我的最终目标是在两个表之间进行一对一映射,其中表A具有用于标识该表和表B中的唯一行的复合键,我想要有主键,它将具有对表A的外键引用,这样如果我们在表A中有条目,那么相同的jobId条目应该在表B中,现在我得到你的观点,我们不能在表B中有单列主键来引用表A中的复合键

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

Tob*_*obb 11

错误不言而喻,为了引用复合主键,您需要一个复合外键.(复合主键表明您需要2个字段的唯一组合来创建密钥 - 然后您不可能仅使用1列引用唯一键.)

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

至于你的Java类,我假设ImportJobManagement持有一个ImportJob,那么这个类不应该引用id,而是引用对象本身,如下所示:

public class ImportJobManagment implements Serializable {
    private ImportJob importJob;
    ...
}
Run Code Online (Sandbox Code Playgroud)

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   |
Run Code Online (Sandbox Code Playgroud)

这里,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   |
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们在B中遇到问题.由于外键必须引用其引用的表中的单行,这显然不起作用.A中哪一行的值是'3'代表的?第一行中的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    |
Run Code Online (Sandbox Code Playgroud)

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

再次更新:

我目前正在阅读JPA认证,并已达到复合键映射的章节.要映射复合主键,您需要一个主键类来映射键的属性.有两种方法可以实现,一种方法是键属性也必须映射到实体本身,另一种方法用作嵌入键.

我将提供代码示例,它们非常适合自己(它使用注释,你也应该这样做.)

第一个示例是基本示例,具有常规id类(未嵌入).在这里,我们有一个Employee实体,其中主键由一个整数id和一个国家组成(如果在不同的国家,两个Employees可以具有相同的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
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

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

另一种方法是通过嵌入式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
}
Run Code Online (Sandbox Code Playgroud)

注意:

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

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