【问题标题】:Cannot make @ManyToOne relationship nullable不能使@ManyToOne 关系可以为空
【发布时间】:2013-03-19 22:40:52
【问题描述】:

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

@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 时,这真的是预期的行为吗?在这种情况下是否有启用可为空的外键的解决方法?

提前谢谢你。

【问题讨论】:

    标签: jpa-2.0 jboss7.x nullable postgresql-9.1 many-to-one


    【解决方案1】:

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      我遇到了这个问题,但我能够通过这种方式解决它:

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

      也许问题来自声明@ManyToOne(optional = true)

      【讨论】:

      • nullable = false?? false?为什么是false,而不是true
      • 当然应该是nullable = true
      • Nullable默认设置为true,无需定义。
      【解决方案3】:

      这很奇怪。

      在 JPA 中,可空参数默认为 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)。幸运的是,我想我即将找到解决方案。我只是在做一些测试,如果成功,我会发布解决方案。感谢您的帮助。
      【解决方案4】:

      在事务内但在保存操作之前,将外键列值显式设置为空。通过这个休眠,永远不要对此外键相关表执行选择查询,并且不要抛出异常“在刷新之前保存瞬态实例”。如果您想有条件地设置“空值”,则执行 1. 使用 repo call get/find 获取并设置值 2. 然后检查条件的获取值并将其设置为 null 。粘贴下面的代码进行测试并找到工作

      // 事务开始
      
         可选 customerObject = customerRepository.findByCustomerId(customer.getCustomerId())
      
         if(customerObject.isPresent())yourEnclosureEntityObject.setCustomer(customerObject)}
      
         否则 {yourEnclosureEntityObject.setCustomer(null)}
      
         yourEnclosureEntityObjectRepository.save(yourEnclosureEntityObject)
      
      // 交易结束

      【讨论】:

      • 生活中总有一种解决方法,也很高兴知道如何
      • 很高兴看到您的编辑开始到位,我已将先前的答案 ["there is a workaround"] 标记为不是答案。感谢您的帮助,但如果您下次一次性完成这项工作以避免标记和反对票,那就更好了。咻!
      • 我是新的贡献者。行 。会做
      猜你喜欢
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-29
      • 1970-01-01
      • 1970-01-01
      • 2021-06-30
      • 2016-09-20
      • 1970-01-01
      相关资源
      最近更新 更多