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

28

我试图持久化一个非常简单的单向一对多关系,但是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?也许这是一个bug。 - Tim Bender
@TimBedner 我也在2.3.3和2.4.0上测试过了,但没有运气。我在EclipseLink Bugzilla上开了一个案例。 - gamliela
你的代码看起来很不错。你想尝试使用Batoo JPA吗?如果失败了,我可以提供帮助。http://batoo.jp - Hasan Ceylan
我已经添加了第一个社区测试用例到Batoo JPA :) 你的用例通过了...https://github.com/BatooOrg/BatooJPA/tree/master/community/src/test/java/org/batoo/jpa/community/test/t1 - Hasan Ceylan
我建议将映射关系移动到Parameter类中。@ManyToOne(cascade={CascadeType.ALL}) @JoinColumn(name="service_id", referencedColumnName="service_id") private Service service; - Riadh
6个回答

41

@JoinColumn上使用nullable = false

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

2
我认为这是最好的答案,因为它几乎没有改变原问题的任何前提。 - Ale Zalazar
谢谢,这解决了我的问题而不改变其他任何东西。 - Bunkerbewohner

15

尝试移除 Parameter 表中 service_id 字段的非空约束。Eclipselink 会在单独的语句中更新单向 1:m 关联列的外键,因此您需要禁用或延迟约束检查。将其改为双向关联将允许 fp 字段与其余参数数据一起更新。


关系上没有非空注释,因此DDL生成不会添加一个。 - Chris
表格是手动创建的,因此就不需要非空约束。双向关系确实可以起作用,但这只是一种解决方法,并没有回答问题。 - gamliela
如果没有非空约束,为什么服务ID没有值就会抛出异常? 如果您删除约束,您将看到EclipseLink稍后更新fk,因为它不受Parameter实体控制。 - Chris
有非空约束。很抱歉,从问题中并不清楚,但我不明白它为什么相关。阅读了https://dev59.com/QWcs5IYBdhLWcg3wjUuR之后,我意识到这可能是答案的一部分... - gamliela
这是EclipseLink的限制/错误。移除非空约束或使关系双向化是解决方法。我授予Chris奖励,以表彰他帮助澄清此问题。完整详情可在此处此处找到。 - gamliela
显示剩余2条评论

2

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


1
我能够通过使用可延迟的外键在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

1

您可以更改Hibernate版本<4.0的持久性,您的代码将正常运行。在此指的是“仅为一对多关系保存/持久化父项,而不是通过单独任务保存/持久化子项集合”的情况下,“正常”。


0

我发现,在这种情况下,外键需要在单独的语句中填充。在我的例子中,我使用了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]

因此,使用nullable=true@JoinColumn会导致错误。
作为替代方案,您可以使用@OneToMany(..., orphanRemoval=true, ...)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接