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

How to update Child entity along with Parent entity in Spring Boot?

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

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session Contact(6)

实体如下,

Parent -> User.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;
}

Child -> 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);
}

因为您在持久性上下文中表示了两次相同的用户(即相同的 ID)。

此行将用户 curUser 加载到持久性上下文中。

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

这一行通过 id 使 user 成为同一用户,但它是不同的时刻。

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);

应该可以解决问题。

关于您的评论:

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

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