JPA级联合并时不应该

时间:2016-10-07 00:34:42

标签: java hibernate jpa

我很难理解这个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);

merge会抛出异常。

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

然后它会抛出异常  org.springframework.orm.jpa.JpaObjectRetrievalFailureException:找不到标识为6aaab891-872d-41e6-8362-314601324847的com.mytest.domain.UserProfile;

为什么甚至会调用此查询?

由于我没有在userprofiles中将cascade类型设置为MERGE,我的期望是JPA / Hibernate会忽略userprofiles set中的实体并且只插入/更新用户记录,不会这样做反对JPA规范?

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

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

1 个答案:

答案 0 :(得分:0)

  

为什么甚至会调用此查询?

这是因为您正在尝试合并实体,在JPA中使用merge()来创建实体managed/attached。要“合并”用户,JPA仍然需要维护它所拥有的引用(UserProfile)。在你的情况下,它并没有试图坚持UserProfile试图获得它的引用来合并User。阅读here

如果使用persist而不是merge,则不应该这样做。