Reputation: 117
I have a RECIPE
table that has OneToMany relationship with the INGREDIENT
table because a single recipe can have many ingredients. The issue is that if a user deletes an ingredient (which sets all fields (ingredient_id
and ingredient
) to NULL by frontend), then the row containing relationship of both the tables RECIPE_INGREDIENT
is deleted but the row in the Ingredient
table still exists. Can't we tell Hibernate to delete that rows also?
Oracle table
create table recipe(id number primary key,
name varchar2(25) unique);
create table ingredient(ingredient_id number(4) primary key,
ingredient varchar2(40));
create table recipe_ingredient(recipe_id number(4),
ingredient_id number(4),
constraint recipe_fk foreign key(recipe_id)
references recipe(recipe_id),
constraint ingredient_fk foreign
key(ingredient_id) references
ingredient(ingredient_id));
Ingredient and Recipe POJO
@Entity
@Table(name = "ingredient", uniqueConstraints={
@UniqueConstraint(columnNames="INGREDIENT_ID")
})
public class Ingredient implements Serializable {
@Id
@Column(name = "INGREDIENT_ID", unique=true, nullable=false)
@SequenceGenerator(name="seq_ingredient", sequenceName="seq_ingredient")
@GeneratedValue(strategy=GenerationType.AUTO, generator="seq_ingredient")
private Integer ingredientId;
@Column(name = "INGREDIENT")
private String ingredient;
/*@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="RECIPE_ID")
private Recipe recipe;*/
//getter and setters
@Entity
@Table(name = "recipe")
public class Recipe implements Serializable {
@Id
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinTable(name = "recipe_ingredient", joinColumns = { @JoinColumn(name = "recipe_id") }, inverseJoinColumns = { @JoinColumn(name = "ingredient_id") })
private List<Ingredient> ingredients;
//getters and setter
}
DAO Code
public class RecipeDaoImpl implements RecipeDao {
public void addRecipe(Recipe recipe) {
getSession().saveOrUpdate(recipe);
}
}
Log that shows that the row in INGREDIENT
table still exists whereas Hibernate is just deleting row from 'RECIPE_INGREDIENT' table.
Please see following that ingredient_id
with null
is deleted. In both cases, it is updating ingredient.recipe_id
as NULL.
Received following from frontend:
RecipeController - Recipe[recipeId=126,name=Sandwich,ingredients=[Ingredient[ingredientId=270,ingredient=Salt],[ingredientId=<null>,quantity=<null>]]]
Hibernate: update RECIPE set NAME=? where RECIPE_ID=?
Hibernate: update ingredient set INGREDIENT=? where INGREDIENT_ID=?
Hibernate: delete from recipe_ingredient where recipe_id=?
Hibernate: insert into recipe_ingredient (recipe_id, ingredient_id) values (?, ?)
So the database table has,
INDREDIENT
INGREDIENT_ID INGREDIENT
271 Salt
272 Sugar
RECIPE_INDGREDIENT
RECIPE_ID INDREDIENT_ID
126 271
Upvotes: 3
Views: 4297
Reputation: 491
I solved this issue by adding insertable = false, updatable = false
as attributes to @JoinColumn
Like this:
@JoinColumn(name="RECIPE_ID", insertable = false, updatable = false)
Upvotes: 4
Reputation: 26067
Orphan Removal in Relationships
When a target entity
in one-to-one
or one-to-many
relationship is removed from the relationship, it is often desirable to cascade the remove operation
to the target entity. Such target entities are considered “orphans,” and the orphanRemoval attribute
can be used to specify that orphaned entities should be removed.
For example, if an RECIPE
has many INGREDIENT
and one of them is removed from the list
, the removed INGREDIENT
is considered an orphan. If orphanRemoval is set to true
, the line item entity will be deleted when the INGREDIENT
is removed from the list
.
The orphanRemoval
attribute in @OneToMany and @oneToOne
takes a Boolean value
and is by default false.
The following example will cascade the remove operation to the orphaned INGREDIENT
entity when it is removed from the relationship:
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval="true")
@JoinColumn(name="RECIPE_ID")
private List<Ingredient> ingredients;
To Add more
orphanRemoval
and DELETE CASCADE.
orphanRemoval is an entirely ORM-specific thing. It marks "child" entity to be removed when it's no longer referenced from the "parent" entity
, e.g. when you remove the child entity from the corresponding collection of the parent entity.
ON DELETE CASCADE is a database-specific thing, it deletes the "child" row in the database when the "parent" row is deleted
.
Upvotes: 1