【问题标题】:Different object with same identifier was already associated with the session具有相同标识符的不同对象已与会话关联
【发布时间】:2015-09-09 12:55:37
【问题描述】:

尝试更新“照片”时出现错误:

  Photo photo = photoRepository.findById(id);
  List<Photo> photos = user.getPhotos();
  user.setPhotos(photos.add(photo));      
  userRepository.update(user);

显示跟随错误:

  Different object with same identifier was already associated with the session

看起来,具有相同标识符的不同对象(第一个:第一行中的照片,第二个:user.getPhotos() 中的照片)。

所以,我无法更新我的用户。 如何避免此错误?

用户存储库:

tx = session.beginTransaction();
user = (User) session.get(User.class, id);
session.getTransaction().commit();

【问题讨论】:

  • 您使用的是 Spring,为什么要手动管理事务?你永远不应该像那样获得一个集合并向它添加对象。您正在泄漏内部状态。您应该在您的 User 上有一个 addPhoto 方法,该方法将 Photo 添加到集合中,并且假设它是双向关系,它也会执行 Photo.setUser
  • 对不起,我改成 user.setPhotos(photos.add(photo));
  • 我已经有了 photo.setUser()。你的意思是手动管理你的交易?你有其他方法吗?
  • 更糟糕的是......您应该允许在外部设置内部集合,这对于托管集合尤其危险。
  • 你应该阅读 cmets...

标签: java spring hibernate


【解决方案1】:

您的代码有缺陷,在 ORM 世界中管理双向关系时,您绝不应该允许对内部状态进行外部修改。目前,您有一种服务方法,它从用户那里提取照片列表并添加新照片。有了这个,你只设置了关系的一侧。您还应该在 Photo 对象上调用 setUser

Photo photo = photoRepository.findById(id);
List<Photo> photos = user.getPhotos();
photo.setUser(user);
user.setPhotos(photos.add(photo));      
userRepository.update(user);

但是,这段代码很容易出错,并且应该在 User 对象旁边,您根本不应该允许调用 setPhotos,因为这可能会破坏所有正确的关系。

public class User {

    public Set<Photo> getPhotos() {
        // Unmodifiable to protect internal state.
        return Collections.unmodifiableSet(this.photos);
    }
    // No setPhotos!

    public void addPhoto(Photo photo) {
       photo.setUser(this);
       this.photos.add(photo);
    }
}

假设您的PhotoUser 在同一个包中,则setUser 可能是包可见的(即setUser 方法中没有publicprotected)。

现在在你的代码中你可以简单地做

Photo photo = photoRepository.findById(id);
user.addPhoto(photo);
userRepository.update(user);

无需让它变得更复杂,添加照片的逻辑现在已经很好地封装和测试了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-16
    • 2013-08-30
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多