【问题标题】:JPA cascading merge when it shouldn'tJPA 级联合并时不应该
【发布时间】:2016-10-07 00:34:42
【问题描述】:

我很难理解这种 JPA 行为,在我看来它似乎不符合规范。 我有 2 个基本实体:

public class User {
  @Id
  @Column(name = "id", unique = true, nullable = false, length = 36)
  @Access(AccessType.PROPERTY)
  private ID id;

  @OrderBy("sequence ASC")
  @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = { CascadeType.REMOVE })
  private final Set<UserProfile> userprofiles = new HashSet<UserProfile>(0);

  //Ommiting rest of fields since they aren't relevant
}


public class UserProfile {
  @Id
  @Column(name = "id", unique = true, nullable = false, length = 36)
  @Access(AccessType.PROPERTY)
  private ID id;

  @NotNull
  @ManyToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "userID", nullable = false, foreignKey = @ForeignKey(name = "FK_UserProfile_User"))
  private User user;

  //Ommiting rest of fields since they aren't relevant
}

如您所见,我只将级联设置为 REMOVE,如果我根本没有设置级联,行为将是相同的。

现在如果我打电话:

User user = new User();
user.setId(UUIDGenerator.generateId());

UserProfile userProfile = new UserProfile();
userProfile.setId(UUIDGenerator.generateId());
userProfile.setUser(user);
user.getUserProfiles().add(userProfile);

em.merge(user);

合并会抛出异常。

我看到 Hibernate 正在对 UserProfile 表执行 SQL 查询: 选择 userprofil0_.userProfileID 作为 userProf1_4_0_,userprofil0_.profileID 作为 profileI3_4_0_,userprofil0_.sequence 作为 sequence2_4_0_,userprofil0_.userID 作为 userID4_4_0_ from UserProfile userprofil0_ where userprofil0_.userProfileID=?

然后会抛出异常 org.springframework.orm.jpa.JpaObjectRetrievalFailureException:无法找到 com.mytest.domain.UserProfile,id 为 6aaab891-872d-41e6-8362-314601324847;

为什么还要调用这个查询?

由于我没有在 userprofiles 中将级联类型设置为 MERGE,我的期望是 JPA/Hibernate 会简单地忽略 userprofiles 集中的实体并仅插入/更新用户记录,这不违反 JPA 规范?

如果我将 cascadetype 更改为 MERGE,事情将按预期工作,并且 User 和 UserProfile 都将添加到数据库中,所以没有问题。让我感到困惑的是,为什么 Hibernate 会查询数据库并错误地指出一个根本不应该合并的实体,因为我没有将它设置为级联。

这更像是我遇到的一个学术场景,当然我可以简单地清除用户配置文件集并且事情会起作用,但我试图理解为什么会发生上述行为,因为我可能错过了一些关键部分有关合并如何工作的信息。无论是否设置了级联类型,它似乎总是会尝试将所有实体附加到会话。

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    为什么还要调用这个查询?

    这是因为您正在尝试合并实体,在 JPA 中,merge() 用于使实体managed/attached。要“合并”用户,JPA 仍需要维护它持有的引用(UserProfile)。在您的情况下,它不是试图坚持UserProfile,而是试图获取对它的引用以合并User。阅读here

    如果您使用持久化而不是合并,则不应发生这种情况。

    【讨论】:

    • 是的,我明白我想做什么,但这是否在 JPA 规范中得到了解释?因为没有在 User 上设置 CascadeType.MERGE 我希望它在合并时简单地忽略 UserProfile 实体。那篇文章非常简单,因为它不涉及多对一关系,并且那里解释的行为很好。我对子记录表示怀疑,因为我在 JPA 或 Hibernate 规范中找不到任何关于此的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    • 2011-08-12
    • 2012-06-17
    • 2012-11-12
    相关资源
    最近更新 更多