如何在JPA中将复合外键的列Map到复合主键的列

gcuhipw9  于 2023-03-23  发布在  其他
关注(0)|答案(3)|浏览(234)

假设有两个表clientdevice具有一个(* 客户端 )对多个( 设备 *)关系。表的简化模式为:

TABLE client (
  id,
  import_date,
  PRIMARY_KEY (id, import_date)
)

以及

TABLE device (
  id,
  import_date,
  client_id,
  PRIMARY_KEY (id, import_date),
  FOREIGN_KEY (client_id -> client.id, import_date -> client.import_date)
)

注意:列device.import_date既是主键的一部分,也是外键的一部分。
不幸的是,我没有在JPA中描述上述结构。
这是我的一个小小的尝试(getter,setter,实现的接口等省略):

public class ImportId {
    private Integer id;
    private LocalDate importDate;
}

@Entity
@IdClass(ImportId.class)
public class Client {
    @Id
    private Integer id;
    @Id
    private LocalDate importDate;
}

@Entity
@IdClass(ImportId.class)
public class Device {
    @Id
    private Integer id;
    @Id
    private LocalDate importDate;
    @ManyToOne
    //@MapsId("importDate") <-- does not work
    //@JoinColumn
    //@JoinColumn           <-- also not working
    private Client client;
}

@MapsId@IdClass一起使用没有什么特别之处,会导致device表中多出一列client_import_date,这是不希望的。
我还尝试将@MapsId@Embeddable结合使用,但没有成功。

org.hibernate.PropertyNotFoundException: Could not locate field name [id] on class [java.time.LocalDate]

有人能帮我解决这个问题吗?
多谢了!

更新1(回应古天乐的做法)

乍一看,这看起来很有希望。Hibernate没有创建“client_import_date”列并正确设置外键。然而,我不能保存设备实体。执行以下代码

// var client = clientRepository.save(new Client(1234, LocalDate.now());
deviceRepository.save(new Device(567, LocalDate.now(), client));

Hibernate生成以下SQL:

INSERT INTO device(client_id, import_date, id) VALUES(?,?,?);

然后尝试绑定以下参数:

[1] as [INTEGER] - [1234]
[2] as [DATE] - [2023-03-22]
[3] as [INTEGER] - [567]
[4] as [DATE] - [2023-03-22]

然后抛出异常Invalid value "4" for parameter "parameterIndex"。当然它失败了,因为没有第四个参数可以绑定。有什么想法为什么Hibernate的参数列表不匹配insert语句?

更新1.1
    • 更新1**中描述的Hibernate异常似乎只在与H2平台配合使用时才会出现,因此在此场景下集成测试可能会中断。测试版本:Hibernate 6.1.7和H2 2.1.214,都是Sping Boot 3.0.4附带的。

使用MariaDB驱动程序,Phil Ku的方法一切正常。跟踪Hibernate输出显示仍然有四个候选参数,选择前三个似乎不是问题。

6rqinv9w

6rqinv9w1#

我可以使模式工作,但我可以插入的唯一方法是使用本机查询。如果你想出一个更好的方法,也许编辑你的原始帖子。

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Device {
    @EmbeddedId
    private DeviceId id;

    @MapsId("clientId")
    @JoinColumns({
            @JoinColumn(name = "clientId", referencedColumnName = "id"),
            @JoinColumn(name = "importDate", referencedColumnName = "importDate")
    })
    @ManyToOne()
    @ToString.Exclude
    private Client client;
}
798qvoo8

798qvoo82#

更新日期:

在尝试复制您的问题后,棘手的部分是Device中的importDate,它作为其Primary Key,也作为ClientForeign Key,以下是一个可能的解决方案。
@JoinColumnsOrFormulas用于Device中的client

@ManyToOne
@JoinColumnsOrFormulas({
    @JoinColumnOrFormula(formula = @JoinFormula(value = "import_date", referencedColumnName = "importDate")),
    @JoinColumnOrFormula(column = @JoinColumn(name = "client_id", referencedColumnName = "id"))
})
private Client client;

Device中设置client时,将importDate设置为client.importDate

public void setClient(Client client){
    this.client = client;
    this.importDate = client.getImportDate();
}

Device.java

@Entity
@IdClass(ImportId.class)
public class Device {

    @Id private Integer id;
    
    @Id
    @Column(name = "import_date")
    private LocalDate importDate;

    @ManyToOne
    @JoinColumnsOrFormulas({
        @JoinColumnOrFormula(formula = @JoinFormula(value = "import_date", referencedColumnName = "importDate")),
        @JoinColumnOrFormula(column = @JoinColumn(name = "client_id", referencedColumnName = "id"))
    })
    private Client client;

    
    public void setClient(Client client){
        this.client = client;
        this.importDate = client.getImportDate();
    }
    
}

希望有帮助。

f2uvfpb9

f2uvfpb93#

当Java在LocalDate类中搜索名为“id”的字段但找不到此类字段时,会发生此错误。解决此错误的方法是确保在数据库操作期间使用正确的字段名称和数据类型。如果使用的是Entity类,请确保其字段已正确注解和配置。
因此,总结一下,您可以通过以下方式解决此错误:
检查Entity类和数据库表,以了解在LocalDate类中未使用“id”字段时发生错误的原因。确保Entity类中的字段已正确注解,例如,正确使用@Id、@GeneratedValue和@Column注解。确保Entity类中的字段名称与数据库表中的列名匹配。通过执行以下步骤,您可以确定错误的原因并解决它。

相关问题