如何在 Spring Boot 中更新子实体和父实体?

Posted

技术标签:

【中文标题】如何在 Spring Boot 中更新子实体和父实体?【英文标题】:How to update Child entity along with Parent entity in Spring Boot? 【发布时间】:2021-03-12 22:37:16 【问题描述】:

我正在尝试使用 JPA 更新父实体和子实体(与父实体一起提供)。我尝试了很多方法,但我做不到。它给了我以下错误。

javax.persistence.EntityExistsException: 一个不同的对象 相同的标识符值已与会话 Contact(6) 相关联

下面是实体,

父 -> 用户.java

@Entity
public class User extends Serializable 

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
    @SequenceGenerator(name = "seq_generator", sequenceName = "seq_users", initialValue = 1, allocationSize = 1)
    private Long id;
    
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    private String street;
    private String city;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "userContact")
    private Contact contact;

孩子 -> Contact.java

@Entity
public class Contact extends Serializable 

    private static final long serialVersionUID = 1L;
    
    @Getter(value = AccessLevel.NONE)
    @Setter(value = AccessLevel.NONE)
    @Id
    @Column(insertable = false, updatable = false)
    private Long id;

    @MapsId
    @OneToOne
    @JoinColumn(name = "id", nullable = false)
    private User userContact;

    private String phone;

在我的 Service.java 中我是这样更新的,

@Override
@Transactional
public User update(Long id, User user) 
    Optional<User> curUser = userRepo.findById(id);
    
    user.setId(curUser.get().getId());
    user.setStreet(curUser.get().getStreet());
    user.setCity(curUser.get().getCity());

    Contact contact = user.getContact();
    contact.setUserContact(user);

    user.setContact(contact);

    return userRepo.save(user);

而更新JSON的payload是这样的,


    "firstName": "FirstName",
    "lastName": "LastName",
    "email": "Email",
    "password": "Password",
    "contact": 
        "phone": "0123456789"
    

那么为什么我不能立即更新呢?插入工作正常,但此更新不起作用。我在这里做错了什么?如果有人可以帮助我,我真的很感激。提前致谢。

这是我尝试的新方法,

@Override
@Transactional
public User update(Long id, User user) 
    Optional<User> curUser = userRepo.findById(id);
    
    curUser.setFirstName(user.getFirstName());
    curUser.setlastName(user.getLastName());
    curUser.setEmail(user.getEmail());
    curUser.setPassword(user.getPassword());

    Contact contact = user.getContact();
    contact.setUserContact(curUser);

    curUser.setContact(contact);

    return userRepo.save(curUser);

【问题讨论】:

如果您有用户和联系人的存储库,请先尝试将联系人保存在联系人存储库中。如果这种方法对您有帮助,请告诉我 @jafarmlp 我试过了,但还是一样的错误。 【参考方案1】:

因为您在持久化上下文中有两次表示相同的用户(即相同的 id)。

这一行在持久化上下文中加载用户curUser

Optional<User> curUser = userRepo.findById(id);

这一行使user 的 id 是同一个用户,但它是一个不同的瞬间。

user.setId(curUser.get().getId());

最后这一行尝试将user 添加到持久化上下文中。

userRepo.save(user);

这将导致两个实例附加到持久性上下文,同时代表具有相同 id 的同一用户,这将导致状态不一致,因为 JPA 不知道要持久化哪个版本。因此这是禁止的,并会引发您所看到的异常。

在你的情况下改变

Optional<User> curUser = userRepo.findById(id);
    
user.setId(curUser.get().getId());

user.setId(id);

应该可以解决问题。

关于你的 cmets:

引用对象也存在同样的问题。您必须确保:加载实体并更改其属性,或者不加载实体,不直接或间接通过引用并保存根据您的请求构造的分离实体。

如果您加载一个实体,然后尝试保存它的分离实例,您将收到此类错误。

【讨论】:

非常感谢您的回答和您的时间。我能够修复它。

以上是关于如何在 Spring Boot 中更新子实体和父实体?的主要内容,如果未能解决你的问题,请参考以下文章

用于过滤两个子实体的 Spring Boot 规范

如何在 Spring Boot 应用程序中将 java lang long 转换为实体类

无法使用 Spring Boot 更新现有实体

如何识别 ERD 上的强弱关系?

Spring Boot MVC - 如何以编程方式生成实体的 etag 值?

具有实体框架 4.1 和父/子关系的存储库模式