JPA - 使用 EclipseLink 保持单向一对多关系失败

Posted

技术标签:

【中文标题】JPA - 使用 EclipseLink 保持单向一对多关系失败【英文标题】:JPA - Persisting a Unidirectional One to Many relationship fails with EclipseLink 【发布时间】:2012-09-27 03:34:06 【问题描述】:

我正在尝试保持一个非常简单的单向一对多关系,但 EclipseLink (2.3.1) 失败。

服务类(父):

@Entity
@Table(name = "tbl_service2")
public class Service implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="service_id")
    public long serviceID;

    @Column(name="name")
    public String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="service_id", referencedColumnName="service_id")
    public Set<Parameter> parameters;

参数类(子): (当然数据库中有“service_id”外键字段,类中没有表示,因为它是单向关系)。

@Entity
@Table(name = "tbl_service_parameters2")
public class Parameter implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="param_id")
    public long parameterID;

    @Column(name="name")
    public String name;

这是实体持久化的代码:

    Service service = new Service();
    service.parameters = new HashSet<Parameter>();
    service.name = "test";
    Parameter param = new Parameter();
    param.name = "test";
    service.parameters.add(param);
    em.persist(service);
    em.flush();

我得到了这个例外:

Internal Exception: java.sql.SQLException: Field 'service_id' doesn't have a default value
Error Code: 1364
Call: INSERT INTO tbl_service_parameters2 (name) VALUES (?)
    bind => [test]

编辑:由于数据的性质,数据库字段 service_id 具有(并且应该具有)非空约束。

这是一个错误还是代码中有问题?

【问题讨论】:

奇怪。您是否尝试过更新版本的 EclipseLink? 2.3.3 也许?也许这是一个错误。 @TimBedner 在 2.3.3 和 2.4.0 上也进行了测试,但没有运气。我在EclipseLink Bugzilla开了一个案例@ 您的代码看起来还不错。您想尝试使用 Batoo JPA 吗?如果失败,我可能会提供帮助。 batoo.jp 我已将第一个社区测试用例添加到 Batoo JPA :) 你的案例通过了...github.com/BatooOrg/BatooJPA/tree/master/community/src/test/… 我建议将映射关系移动到Parameter类。 @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name="service_id", referencedColumnName="service_id") 私有Service服务; 【参考方案1】:

@JoinColumn上使用nullable = false

@JoinColumn(name = "service_id", nullable = false)

【讨论】:

我认为这是最好的答案,因为它几乎不会改变原始问题的任何前提。 谢谢,这解决了我的问题而无需更改任何其他内容。【参考方案2】:

尝试删除 Parameter 表的 service_id 字段上的 not null 约束。 Eclipselink 将在单独的语句中更新单向 1:m 连接列的外键,因此您需要禁用或延迟约束检查。使其成为双向将允许 fp 字段使用其余参数数据进行更新。

【讨论】:

是否假设 OP 正在手动创建表?我假设表创建也被委托给 EclipseLink。 关系上没有非空注解,所以DDL gen不会加一个。 表是手动创建的,因此非空约束与 AFAIN 无关。使关系双向确实有效,但这是一种解决方法,不能回答问题。 如果没有非空约束,为什么 service_id 的异常没有值?如果删除约束,您将看到 EclipseLink 稍后更新 fk,因为它不受 Parameter 实体的控制。 存在非空约束。很抱歉,问题并不清楚,但我不明白为什么它是相关的。阅读***.com/questions/12755380 后,我意识到这可能是答案的一部分......【参考方案3】:

您可以将您的持久性更改为休眠版本

【讨论】:

【参考方案4】:

我能够通过使用可延迟外键使其在 Oracle 中工作。

例子:

ALTER TABLE my_table ADD CONSTRAINT my_constraint_name FOREIGN KEY (my_table_column) REFERENCES foreign_key_table  (foreign_key_table_column) DEFERRABLE INITIALLY DEFERRED

【讨论】:

【参考方案5】:

默认情况下,@JoinColumn 上的 nullable 为 true,在将数据以一对多关系持久化的同时,我们需要将 nullable 设置为 false,以避免在运行时发生数据违规异常。

【讨论】:

【参考方案6】:

我发现,在这种情况下,外键会填写在单独的语句中。在我的示例中,我使用了 Address 实体和 customer_id 作为外键。

2014-07-08T20:51:12.752+0300|FINE: INSERT INTO ADDRESS (address_id, street, city, region) VALUES (?, ?, ?, ?)
    bind => [10, foo, foo, foo]
2014-07-08T20:51:12.753+0300|FINEST: Execute query InsertObjectQuery(ua.test.customer.Address@28cef39d)
2014-07-08T20:51:12.757+0300|FINEST: Execute query DataModifyQuery(sql="UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)")
2014-07-08T20:51:12.757+0300|FINE: UPDATE ADDRESS SET customer_id = ? WHERE (address_id = ?)
    bind => [151, 10]

因此,将@JoinColumnnullable=true 结合使用会导致错误。

您也可以使用@OneToMany (..., orphanRemoval = true, ...)

【讨论】:

以上是关于JPA - 使用 EclipseLink 保持单向一对多关系失败的主要内容,如果未能解决你的问题,请参考以下文章

JPA 使用指南 /Eclipselink/JPA 实体生成器

JPA - EclipseLink - 如何更改默认模式

JPA + EclipseLink+ HSQLDB 不创建表

JPA 从 EclipseLink 到哪个提供商?

使用 JPA (Eclipselink) 在 H2 数据库中执行全文搜索

jpa2/eclipselink 的合适 DAO 结构是啥?