【问题标题】:Hibernate self-reference entity as non null column休眠自引用实体作为非空列
【发布时间】:2013-11-29 04:07:30
【问题描述】:

我正在使用 Hibernate 5 和 Spring 3.2.4。我正在设计一个用户实体,我想在其中包含对创建实体的用户的引用 - 所以是一个自我引用。自引用本身并没有太大问题,但我想将该字段指定为非空。这可能吗?如果该字段不为空,因为引用的实体尚不存在,如何在数据库中创建第一个条目?

例如:

@Entity
public class User {

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

    @NotNull
    private String username;

    private String password;

    @NotNull
    private User user;


    // getters and setters omitted for brevity
}

如果我尝试:

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,除非我有一个可以引用的非空用户。但是在一个空的数据库中,没有这样的条目。

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

【问题讨论】:

  • 您是否尝试过使用 @ManyToOne 指定映射?我也没有看到标识符字段,你有没有像 setter 一样省略它?
  • @elusive-code - 我更新了没有@Roo 注释的实体以显示附加文件。是的 - 为了简洁起见,我省略了 getter/setter。 @ManyToOne 和@NotNull 之间的区别,据我了解,@ManyToOne 将用于 DDL 生成和持久化 bean 时,但是即使不持久化 bean,@NotNull 也可用于 bean 验证。跨度>

标签: java hibernate jpa persistence hibernate-5.x


【解决方案1】:

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

  1. 如前所述,使用@ManyToOne 注解。
  2. AFAIK、@NotNull 注释(或@Column 中的nullable 字段)不影响映射,仅影响 DDL 生成。我不使用域模型中的 DDL 生成,我从不指定这个。相反,我使用 @ManyToOneoptional 字段。
  3. 您使用什么标识符生成策略?如果自动增量,则使用NOT NULL 约束是不可能进行自引用的。所以要么使用基于序列的标识符生成器,​​要么删除约束。我会先用。
  4. 正如我提到的,当您有NOT NULL 约束时,将@ManyToOneoptional 字段设置为false。否则,Hibernate 会尝试进行两个查询:插入 createdBy_id 设置为 NULL,然后更新 createdBy_id。第一个查询失败并启用了NOT NULL 约束。

【讨论】:

  • 我已经编辑了实体以提供没有@Roo 注释的示例。是的 - 我正在为此表使用自动增量,没有考虑使用其他一些 GUID 策略。我将尝试一个基于序列的标识符。然而,据我了解,Hibernate 注释和@NotNull 之间的区别在于前者将确保 DB 模式设置,而后者即使不持久化到 DB 也将用于 bean 验证。
  • 我由自动生成更改为表生成(MySQL 不支持序列)。现在似乎可以按需要工作。谢谢!
【解决方案2】:

我找到了解决方案。您必须为您的 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 在插入时就已经知道了。

【讨论】:

    【解决方案3】:

    u 的字段createdBy 不能为空,因为注解@NOTNULL。但是u 指的是它自己,并且在保存之前它不会持续存在。 您可以为u 设置另一个持久的User,而不是它本身。

    【讨论】:

    • 您的假设对于自动增量主键生成是正确的。但是当使用基于序列的 PK 生成器时,ID 在插入之前就已经知道了。所以确实有可能执行有效的INSERT 查询。
    • 就像我在原始帖子中所说的那样,我知道这是导致问题的原因。但是问题仍然存在 - 如何创建表中的第一行?没有其他用户可以分配它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多