Reputation: 924
I'm using Spring Data with CrudRepository
. I'm trying to save Parent with Cascade to children and I'm giving Hibernate the possibility to merge children entities, but I'm getting error a different object with the same identifier value was already associated with the session
. That is happening probably when he has persisted two parent entities which has other children entities (RecipeIngredients
). I'm trying to override equals and hashcode to focus only on id
and name
, but it nothing change. Recipe
object are the same but List<RecipeIgredients>
are different. Any ideas on how to solve it?
Example:
This is my existed object:
{
"id": 100,
"name": "salat",
"ingredients": [
{
"ingredient": {
"id": 100,
"name": "banana"
},
"count": 2
},
{
"ingredient": {
"id": 1,
"name": "eggs"
},
"count": 1
}
]
}
And I want to update it to one below (remove one ingredient):
{
"id": 100,
"name": "salat",
"ingredients": [
{
"ingredient": {
"id": 100,
"name": "bannana"
},
"count": 2
}
]
}
Parent:
@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;
}
Children in the mid-table
@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());
}
}
Children
@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;
}
Upvotes: 3
Views: 1853
Reputation: 85
I can give you example of one of my projects i will hope it will help:
the parent:
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 + '\'' +
'}';
}
}
the child:
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;
}
}
in my case i just need to get the entity for example accountSerice.getById(1);
and then in my case I just need to to do account.setTasks(tasks); // tasks is just list of the childs @see the setTasks() at the account(parent) entity
Upvotes: 1