2

我在驱逐实体时遇到问题,但对其所做的更改仍会更改数据库。这是我的 DAO 中方法的一部分。

@Entity
public class Profile {
    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "PROFILE_ID")
    @LazyCollection(LazyCollectionOption.FALSE)
    private List<Avatar> avatars;

    ...
  }

在 DAO 方法中:

Profile profile = getProfile(...);

// Clear from hibernate
currentSession.evict(profile);
profile.setId(null);

for (Avatar a : profile.getAvatars()) {
    currentSession.evict(a);
    a.setId(null);
}

currentSession.save(profile); // save a copy of Profile (not update)

前:

PUBLIC.PROFILE
  ID, DOMAIN, STATUS
  1, "test", "MEMBER"

PUBLIC.AVATAR
  ID, NAME, PROFILE_ID
  1, "main", 1

后法

PUBLIC.PROFILE
  ID, DOMAIN, STATUS
  1, "test", "MEMBER"
  2, "test", "MEMBER"

PUBLIC.AVATAR
  ID, NAME, PROFILE_ID
  1, "main", null
  2, "main", 2

如您所见,AVATAR 中的原始行现在有一个空外键。

为什么?这发生在使用 Unitils 和 Spring 的单元/集成测试中这可能会影响 Hibernate DAO 的工作方式。

这一切都在内存中的 H2 数据库中。


添加一行后

profile.setAvatars(new ArrayList<>(profile.getAvatars());

有用 ...

所以我猜问题是 Hibernate 的实现List,但这怎么会影响行为呢?

4

1 回答 1

1

编辑:第一个答案很愚蠢,因为@LazyCollection(LazyCollectionOption.FALSE)

我可以重现和修复,但我无法理解引擎盖下实际发生的事情......

首先会发生什么(在调试器下监视):

由于avatars集合是急切的,profile.getAvatars()已完全填充并且是一个 Hibernate 集合(在我自己的测试中,它是一个PersistentBag

profile被驱逐时,它的所有化身也被驱逐(至少在 Hibernate 4.1.9 Final 中)。

总之currentSession.save(profile)很了不起,插入了一个新Profile的,也插入了一个新的Avatar。但是在事务提交之后,Hibernate 决定执行著名的update Avatar set profile_id = null where profile_id = 1;:-(

接下来修复:

我想 Hibernate 很惊讶地发现一个新实体已经拥有PersistentBag一个集合。所以我创建了一个简单ArrayList的附加电流Avatars,并将其放入profile

List<Avatar> avatars = new ArrayList<Avatar>();
for (Avatar a : profile.getAvatars()) {
    currentSession.evict(a); // in fact useless but harmless
    a.setId(null);
    avatars.add(a);
}
profile.setAvatars(avatars);

而且......一切都很好,Hibernate 不再发出有问题的更新!

所以原因似乎是PersistentBag在一个新实体中,但我无法想象在 Hibernate 内部实际发生了什么。

于 2014-07-15T11:49:54.320 回答