如何在Spring Data JPA中合并子实体

3
我正在使用带有CrudRepository的Spring Data。我试图使用级联保存父对象和子对象,并且我让Hibernate有可能合并子实体,但是我遇到了错误a different object with the same identifier value was already associated with the session。这可能发生在已持久化具有其他子实体(RecipeIngredients)的两个父实体时。我尝试重写equals和hashcode,只关注idname,但没有任何改变。 Recipe对象相同,但List<RecipeIgredients>不同。有什么解决方法吗?
例子:
这是我的现有对象:
{
  "id": 100,
  "name": "salat",
  "ingredients": [
    {
      "ingredient": {
        "id": 100,
        "name": "banana"
      },
      "count": 2
    },
    {
      "ingredient": {
        "id": 1,
        "name": "eggs"
      },
      "count": 1
    }
  ]
}

我希望将其更新为以下版本(去掉一个成分):

{
  "id": 100,
  "name": "salat",
  "ingredients": [
    {
      "ingredient": {
        "id": 100,
        "name": "bannana"
      },
      "count": 2
    }
  ]
}

父元素:

@Entity
@Data
public class Recipe {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "recipe_generator")
@SequenceGenerator(name="recipe_generator", sequenceName = "recipe_seq")
@Column(name = "id", nullable = false)
private Long id;

@NaturalId
@Column
private String name;

@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredients> ingredients;

}

中下游参战者

@Entity
@Data
public class RecipeIngredients implements Serializable {

@EmbeddedId
private RecipeIngredientsId recipeIngredientsId;

@ManyToOne(fetch = FetchType.LAZY)
@MapsId("recipeId")
private Recipe recipe;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@MapsId("ingredientId")
private Ingredient ingredient;

@Column
private Integer count;

public RecipeIngredients(Recipe recipe, Ingredient ingredient) {
    this.recipe = recipe;
    this.ingredient = ingredient;
    this.recipeIngredientsId = new RecipeIngredientsId(recipe.getId(), ingredient.getId());
}

}

子节点

@Entity
@Data
public class Ingredient {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ingredient_generator")
    @SequenceGenerator(name="ingredient_generator", sequenceName = "ingredient_seq")
    @Column(name = "id", updatable = false, nullable = true)
    private Long id;

    @NaturalId
    @Column(unique = true)
    private String name;

}
1个回答

1
我可以给你展示我的一个项目作为例子,希望能有所帮助:
父级元素:
package com.yoav.todolist.models;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "accounts")
public class Account {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @OneToMany(
            mappedBy = "account",
            orphanRemoval = true,
            cascade = CascadeType.ALL
    )
    private List<Task> tasks = new ArrayList<>();

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public Account() {
    }

    public void removeTask(Task task) {
        tasks.remove(task);
        task.setAccount(null);
    }

    public void addTask(Task task) {
        tasks.add(task);
        task.setAccount(this);
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks.forEach(i -> i.setAccount(null));
        this.tasks.clear();

        tasks.forEach(i -> {
            i.setAccount(this);
            addTask(i);
        });
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object account) {
        return ((Account)account).getUsername().equals(this.username);
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

这个孩子:

package com.yoav.todolist.models;

import com.fasterxml.jackson.annotation.JsonIgnore;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private int id;

    @Column(name = "task")
    private String task;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    private Account account;

    @Temporal(TemporalType.DATE)
    @Column(name = "date_of_creation_task")
    private Date date;

    public Task(String task) {
        this.date = new Date();
        this.task = task;
    }

    public Task() {
        this.date = new Date();
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTask() {
        return task;
    }

    public void setTask(String task) {
        this.task = task;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    public boolean equals(Object task) {
        return ((Task)task).getId() == this.id;
    }

    @Override
    public String toString() {
        return "Task{" +
                "id=" + id +
                ", task='" + task + '\'' +
                '}';
    }

    @Override
    public int hashCode() {
        return 31;
    }

}


在我的情况下,我只需要获取实体,例如accountSerice.getById(1);,然后在我的情况下,我只需要执行account.setTasks(tasks); // tasks仅是子项列表 @see账户(父项)实体的setTasks()

1
好的,我知道情况了。你的解决方案有效,谢谢你的回复。但这是因为有持久性,你首先从数据库中检索对象,然后从列表中删除它。我想在子对象持久化之前将它们合并。我认为Hibernate可以帮我完成这个任务,但就像我们看到的那样,它没有实现。 - Adriano

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接