I have the following entities in a one-to-many relationship:
#Entry
class Parent {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Collection<Child> children;
...
}
and
#Entry
class Child {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
...
#Transient
private Parent parent;
...
}
So – out of these, there are the following 2 entity tables in the database
parent(id, ...)
child(id, ..)
and the relationship table between these two entities-- due to that #OneToMany relationship
parent_child(parent_id, child_id)
suppose parent_id=4 has the child_id=7 and thus parent_child table has the row (parent_id=4, child_id=7).
When i delete child id =7, isn’t the (parent_id=4, child_id=7) in parent_child table supposed to be deleted as part of it? I’m seeing that row in parent_child table even after the corresponding child is deleted from the child table.
I’m using the repository (implementing CrudRepository) for deleting that child.
//////////////
UPDATE:
by parent_child(parent_id, child_id), i'm referring to the relationship table that hibernate is generating behind the scenes to maintain the relationship between parent and child tables.
went into this table out of curiosity directly on SQL. and these are what i'm seeing. i expected (still do) the (parent_id=4, child_id=7) row would disappear now that child_id=7 fell off the face of the Earth. but didn't.
You have to mapped your entity class like this.
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Parent parent;
#OneToMany(mappedBy = "parent",cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Collection<Child> children;
Remove
#Transient
private Parent parent;
And do #ManyToOne mapping in Child Entity.
Related
I have a jpa entity with parent-child relationship(1:n).
Any sort of update on the entity results in jpa trying to update each and every column, even if there is no change in the value. I'm confused about the update behaviour for child enitities, I guess same would be happening with those also.
I went through #DynamicUpdate over entities but I do not know how they behave in case of child-entities.
public class Tutorial extends Auditable<String> implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private string description;
#OneToMany(
fetch = FetchType.LAZY,
mappedBy = "tutorial",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Collection<Modules> Modules = new HashSet<>();
// Getter/Setters
}
How to come up with an approach so that only necessary fields/columns and child entities and their columns are update on each save.
I have parent-children unidirectional relation as follows:
#Entity
#Table(name = "PARENT")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int parentId;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "parent_id", nullable = false)
private List<Child> children;
}
#Entity
#Table(name = "CHILD")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "PARENT_ID", insertable = false, updatable= false)
private int parentId;
//some other field
}
I create an instance of the parent, assign a list of children to it and try to persist it and it works well.
Parent p = new Parent();
List<Child> children = new ArrayList<Child>();
Child c = new Child();
children.add(c);
p.addChildren(children);
em.persit(p);
em.flush();
When I try to save via Child entity separately, I see that the insert query is trying to insert null to column PARENT_ID in Child Table and results in
Child c = new Child();
c.setId(78987);
c.setParentId(12345);
em.persist(c);
em.flush();
Exception while saving Child Entity independent of Parent table. The Child entity that Im trying to insert is related to the Parent entity that exists already.
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("MY_SCHEMA"."PARENT"."PARENT_ID")_
Is it not possible to save Child entity directly after defining the relation as Unidirectional?
It will work when you make the change that #Shekhar Khairnar also mentioned.
It works well when you add a child to the parent but does not work when defining a child.
You would do that when the responsibility of creating/updating the referenced column isn't in the current entity, but in another
entity.
If you are going to use unidirectional and add a child object, you should remove the insertable = false definition.
However, such usage may result in data inconsistency. You should pay attention.
In addition, you do not need to give an id when defining a child, because this will be created automatically according to your model and will not take the value you give.
I have parent and child classes.
Parent have a field:
#OneToMany(mappedBy="parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Set<Child> childs;
Child
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "parent_id", referencedColumnName="id")
private Parent parent;
My controller
#PostMapping("/parent)
public Parent createParent(#RequestBody Parent parent) {
return parentRepo.save(parent);
}
it saves parent and childs info EXCEPT parent_id in the child table. parent_id is always null. Is it possible to workaround this problem without deleting mappedBy="parent" option and using additional tables?
I have a design and scenario entity.
I'm getting an error when removing a Design that contains one or more scenarios.
The design entity looks like:
#Entity
public class Design {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(columnDefinition = "LONGBLOB")
private byte[] image;
#OneToMany(mappedBy = "design", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Scenario> ScenarioSet;
The scenario entity looks like:
#Entity
public class Scenario {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "design_ID")
private Design design;
As you can see A design can have more than one scenarios.
And Design is responsible for the relation.
My code to save a scenario:
Design design = this.designService.getDesignById(designID);
scenario.setDesign(design);
this.scenarioService.saveScenario(scenario);
Saving it isn't a problem. I'm saving it this way because the scenario doesn't have an ID at first.
The error i'm getting:
Cannot delete or update a parent row: a foreign key constraint fails (`db`.`scenario`, CONSTRAINT `FKqmttw6jic4aplswy08wtkj5r7` FOREIGN KEY (`design_id`) REFERENCES `design` (`id`)) 0.016 sec
This lets me think that It isn't cascading when I remove the Design.
Add orphanRemoval=true to your scenario list mapping in the Design entity:
#OneToMany(mappedBy = "design", cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY)
private Set<Scenario> ScenarioSet;
CascadeType.ALL (or precisely CascadeType.REMOVE) serve for cascading remove operation when you take an item from the collection and save the owning entity (Design in this case). To tell Hibernate to remove items in the collection when the owning entity (Design) is removed, you need to use the orphanRemovalattribute:
https://docs.oracle.com/cd/E19798-01/821-1841/giqxy/
I have a model that looks like this:
#Entity
#Table(name = "A")
class A {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "A_categories", joinColumns = {
#JoinColumn(name = "A_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "category_id",
nullable = false, updatable = false) })
private List<Category> categories;
}
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
So there is a many-to-many relationship between A and Category. Now categories are static, and cannot be changed by a user. From the UI, the user will try to save an entity A, and each can have one or more categories. So the JSON that comes back looks a little like this:
{A: {categories: [{id: 1}, {id: 2}, {id: 3}]}}
Now when I try to save this A object (after jackson has unmarshalled to java), I just want entries to be made in the join table, A_categories, for each category the new entity has.
However, the Category entity itself also gets updated. So if you notice, the JSON does not have any category_name, and so the database entry for each Category will also get updated to a null entry for the name.
How can I prevent this from happening?
Two different approaches:
1) Set managed categories before merging.
a.setCategories(readAllByIds(a.getCategories()))
private Collection<Category> readAllByIds(Collection<Category> categories) {
Collection<Category> result = new ArrayList();
for (Category category : categories) {
result.add(entityManager.getReference(Category.class, category.getId()));
}
return result;
}
EntityManager.getReference returns proxy, so the additional benefit is that no database round trips are executed for reading the associated categories.
With this solution you are not merging the deserialized categories into the persistence context, thus Hibernate will not synchronize their state with the database.
2) Do not cascade any operations from A to categories (remove cascade attribute).
This way, neither PERSIST nor MERGE will be cascaded and Hibernate will just use ids of the detached Category instances to store the data into the relationship table.
Sidenote: Generally, cascading REMOVE or ALL in a many-to-many association makes no sense (if you remove an A you probably don't want to remove all the categories it belongs to).
#Column has the attributes insertable and updatable. You can set them to false:
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id", insertable=false, updatable=false)
#GeneratedValue
private Integer id;
#Column(name = "category_name", insertable=false, updatable=false)
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
You can try adding this
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories", cascade=CascadeType.DETACH)
private List<A> a;
}
with the cascade.DETACH should not save changes when you save A entity, but let me know if is not working to make an example modifying the ManyToMany relationship with this DETACH action