无法将@ManyToOne关系设置为可空。

24

我有一个多对一的关系,我希望它是可空的:

@ManyToOne(optional = true)
@JoinColumn(name = "customer_id", nullable = true)
private Customer customer;

很遗憾,JPA会一直将我的数据库列设置为NOT NULL。有人能解释一下吗?有没有办法让它工作?请注意,我使用JBoss 7、JPA 2.0与Hibernate作为持久性提供程序以及PostgreSQL 9.1数据库。

编辑

我找到了问题的原因。显然,这是由于我在引用实体"Customer"中定义主键的方式导致的。

@Entity
@Table
public class Customer { 
    @Id
    @GeneratedValue
    @Column(columnDefinition="serial")
    private int id;
}

在使用@Column(columnDefinition="serial")作为主键时,似乎会自动将引用它的外键设置为NOT NULL。在指定列类型为serial时,这是否真的是预期的行为?在这种情况下,有没有解决可空外键的方法?

提前感谢您。

4个回答

24

我找到了解决我的问题的方法。在实体Customer中定义主键的方式是正确的,问题在于外键的声明。应该这样声明:

@ManyToOne
@JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;

如果省略属性columnDefinition="integer",外键将默认设置为源列:具有自己序列的非空序列。当然,这不是我们想要的,因为我们只想引用自动增量ID,而不是创建新的。

此外,似乎还需要属性name=customer_id,在进行一些测试时我观察到这一点。否则,外键列仍将设置为源列。在我看来,这是一种奇怪的行为。欢迎评论或补充信息以澄清此事!

最后,此解决方案的优点是ID由数据库(而不是JPA)生成,因此在手动插入数据或通过脚本插入数据(通常在数据迁移或维护中发生)时我们不必担心它。


6

我遇到了这个问题,但是我通过以下方法解决了它:

@ManyToOne
@JoinColumn(nullable = true)
private Customer customer;

也许问题出现在声明了@ManyToOne(optional = true)

6
nullable = false表示某个属性或字段不允许为空。为什么是 false 而不是 true呢?这是因为通常情况下,我们希望确保数据的完整性和一致性,因此该属性应设置为“不可空”,即 false - Andremoniy
一定要写成 nullable = true - elquimista
1
默认情况下,Nullable 被设置为 true,无需定义它。 - Michał Stochmal

0

这很奇怪。

JPA中,nullable参数默认为true。我一直使用这种配置,它运行良好。如果您尝试保存实体,则应该成功。

您是否尝试删除为此关系创建的表格?也许您有带有该列的旧表格?

或者,您可以尝试在其他代码块中找到解决方案,因为这是正确的配置。

注意:我已经在使用JPA2和Hibernate的PostgreSQL上尝试过此配置。

编辑

在这种情况下,也许您可以尝试稍微不同的主键定义。

例如,您可以使用以下定义:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column()
private Long id;

而且PostgreSQL将会生成

id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)

如果这足够好的话,你可以尝试这个解决方案。

这确实是我考虑过的一个解决方案。不幸的是,它不能从数据库本身生成ID,而这是我在手动插入数据时需要的功能(例如使用Hibernate的import.sql)。幸运的是,我想我即将找到一个解决方案。我正在进行一些测试,如果成功了,我会发布解决方案。感谢您的帮助。 - vcattin

0

在事务内但在保存操作之前,显式将外键列的值设置为null。通过这种方式,Hibernate不会为此外键相关表执行选择查询,并且不会抛出“在刷新之前保存瞬态实例”的异常。如果您想有条件地设置“null值”,则执行以下操作:1.使用repo调用get/find获取并设置该值2.然后根据条件检查获取的值并相应地将其设置为null。下面是已测试并发现有效的代码:

// 事务开始
Optional<Customer> customerObject = customerRepository.findByCustomerId(customer.getCustomerId())
if(customerObject.isPresent())yourEnclosingEntityObject.setCustomer(customerObject)}
else {yourEnclosingEntityObject.setCustomer(null)}
yourEnclosingEntityObjectRepository.save(yourEnclosingEntityObject)
// 事务结束

生活中总有解决问题的方法,知道如何解决问题会更好。 - Antonino
很高兴看到您开始进行编辑,我已经将之前的答案 ["有一种解决方法"] 标记为不是一个答案。感谢您的帮助,但下次最好一次性完成工作,以避免被标记和投票。再见! - Antonino
我是一位新的贡献者。好的,我会做的。 - stackoverflow

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