Java 将自引用实体休眠为非空列

Java 将自引用实体休眠为非空列,java,hibernate,jpa,persistence,hibernate-5.x,Java,Hibernate,Jpa,Persistence,Hibernate 5.x,我正在使用Hibernate5和Spring3.2.4。我正在设计一个用户实体,我想在其中包含一个对创建该实体的用户的引用,也就是一个自引用。自引用本身没有太大问题,但我想将字段指定为非null。这可能吗?如果字段为非null,因为引用的实体不存在,那么如何在DB中创建第一个条目 例: 如果我尝试: User u = new User(); u.setUsername("username"); u.setCreatedBy(u); 并尝试保持u,我会收到以下错误消息: Caused by: o

我正在使用Hibernate5和Spring3.2.4。我正在设计一个用户实体,我想在其中包含一个对创建该实体的用户的引用,也就是一个自引用。自引用本身没有太大问题,但我想将字段指定为非null。这可能吗?如果字段为非null,因为引用的实体不存在,那么如何在DB中创建第一个条目

例:

如果我尝试:

User u = new User();
u.setUsername("username");
u.setCreatedBy(u);
并尝试保持
u
,我会收到以下错误消息:

Caused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.domain.User.createdBy -> com.domain.User
我知道Hibernate正在抱怨它不能引用一个临时实例(即:
u
),但是我不能持久化
u
,除非我可以引用一个非空用户。但是在空数据库中,没有这样的条目


这种配置是不可能的吗?或者有办法解决这个问题吗?

由于注释
@NOTNULL
,由
u
创建的
字段不能为空。但是
u
指的是它自己,它在保存之前不会持久存在。
您可以为
u
设置另一个持久化的
用户,而不是它本身。

我不理解这个Roo注释,也不使用特定于Hibernate的注释,只使用JPA。我对自我介绍没有任何问题。但我有一些提示:

  • 如前所述,使用
    @ManyToOne
    注释
  • 好的,
    @NotNull
    注释(或
    @Column
    中的
    null
    字段)不影响映射,只影响DDL生成。我不使用从域模型生成DDL,我从不指定这一点。相反,我使用了
    @ManyToOne
    可选
    字段
  • 您使用什么标识符生成策略?如果自动递增,则不可能使用
    NOT NULL
    约束进行自引用。所以,要么使用基于序列的标识符生成器,要么删除约束。我会先用
  • 正如我提到的,当您有
    非空
    约束时,将
    @ManyToOne
    可选
    字段设置为
    。否则,Hibernate会尝试进行两次查询:将
    createdBy_id
    设置为
    NULL
    时插入,然后更新
    createdBy_id
    。第一个查询在启用了
    非NULL
    约束的情况下失败

  • 我找到了解决办法。必须为ID使用序列生成器(而不是默认的自动生成ID)。然后它就起作用了

    @Entity
    public class UserModel {
      /**
       * We must use a sequence for generating IDs,
       * because of the self reference .
       * https://vladmihalcea.com/2014/07/15/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/
       */
      @Id
      @GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)
      @SequenceGenerator(name = "sequence", allocationSize = 10)
      private Long id;
    
      /** reference to a parent user, e.g. the manager */
      @ManyToOne(optional = false)
      @NotNull
      UserModel parentUser;
    
      [...]
    
    原因如下:当Hibernate尝试插入新用户时,它还尝试验证对parentUser的引用。但对于我们要插入的第一个用户,这将失败,或者当用户引用自己时,也将失败


    但是,当使用序列生成ID时,新的/下一个ID在插入时就已经知道了。

    您是否尝试过使用@ManyToOne指定映射?此外,我没有看到标识符字段,您是否像setters一样省略了它?@难以捉摸的代码-我在没有
    @Roo
    注释的情况下更新了实体以显示其他文件。是的,为了简洁起见,我省略了getter/setter。根据我的理解,@ManyToOne和@NotNull之间的区别在于,@ManyToOne将用于DDL生成和持久化bean时,然而@NotNull可以用于bean验证,即使在不持久化bean时也是如此。自动增量主键生成的假设是正确的。但当使用基于序列的PK生成器时,ID在插入之前就已经知道了。因此,执行有效的
    INSERT
    查询确实是可能的。正如我在最初的帖子中所说,我知道这就是问题的根源。但是问题仍然存在——如何创建表中的第一行?没有其他用户可以分配它。我已经编辑了实体,以提供没有
    @Roo
    注释的示例。是-我正在为此表使用autoincrement,没有考虑使用其他GUID策略。我将尝试使用基于序列的标识符。然而,据我所知,Hibernate注释和
    @NotNull
    之间的区别在于前者将确保DB模式设置,而后者将用于bean验证,即使不会持久化到DB。我通过自动生成更改为表生成(MySQL不支持序列)。现在看来一切如愿。谢谢
    @Entity
    public class UserModel {
      /**
       * We must use a sequence for generating IDs,
       * because of the self reference .
       * https://vladmihalcea.com/2014/07/15/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/
       */
      @Id
      @GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)
      @SequenceGenerator(name = "sequence", allocationSize = 10)
      private Long id;
    
      /** reference to a parent user, e.g. the manager */
      @ManyToOne(optional = false)
      @NotNull
      UserModel parentUser;
    
      [...]