Hibernate JPA persist saved child entity with null parent id - java

I have two entities:
parent :
public class UserGroup{
#OneToMany(fetch = FetchType.EAGER, mappedBy = "userGroup", cascade = CascadeType.ALL)
private List<User> users;
}
and child:
public class User{
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "user_group_id")
private UserGroup userGroup;
}
when i am trying to save UserGroup with one User in list of users, with this method:
#Transactional
public E save(E e) {
em.persist(e);
em.flush();
em.refresh(e);
return e;
}
my parent and child is getting saved, but user_group_id in child object is null.
Is there any solution?

By considering you are giving UserGroup object along with list of User to save method: so your code should be:
em.save(userGroup);
for(User user : UserGroup.getUsers())
{
user.setuser_group_id(userGroup.getUserGroupId());
em.save(user);
}

You have a bidirectional relationship. The correct way of saving it is putting both references in the entities.
You should do:
userGroup.getUsers().add(user);
user.setUserGroup(userGroup);
entityManager.persist(userGroup);

In your parent setter method of child, do this.
public void setChildren(Collection<Child> children) {
this.children = children;
for(Child child: this.children) {
child.setParent(this)
}
}
This should solve.

Related

Spring JPA how to persist parent when the child is persisted in ManyToMany relationship

I have a basic project with a many to many relationship. There are 2 classes Parent and Child, the owner of the relationship is the class Parent. When parent entities are persisted child entities are also persisted (which is the desired behavior). But at the opposite when the child entities are persisted, the parent entities are not persisted.
How do I get the parent entities persisted at the same time than the child entities? The code below give the spring-boot class that allow to reproduce the issue, after the logger.info("---> 2nd fetch all parents"); I expect to have 5 parents but I have only two.
Here are my entity classes :
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
// #JoinTable => owner of the relationship
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "parent_child",
joinColumns = #JoinColumn(name = "parent_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "child_id", referencedColumnName = "id"))
private Set<Child> children;
}
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#ManyToMany(mappedBy = "children")
private Set<Parent> parents;
// getters and setters
}
The repositories
public interface ChildRepository extends JpaRepository<Child, Long> {}
public interface ParentRepository extends JpaRepository<Parent, Integer> {}
The springboot application
#SpringBootApplication
public class Application implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Autowired
private ParentRepository parentRepository;
#Autowired
private ChildRepository childRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
#Transactional
public void run(String... strings) throws Exception {
// save a couple of parents
Child childA = new Child("Child A"); Child childB = new Child("Child B"); Child childC = new Child("Child C");
Parent parentA = new Parent("Parent A", new HashSet<>(Arrays.asList(childA, childB))); Parent parentB = new Parent("Parent B", new HashSet<>(Arrays.asList(childA, childC)));
parentRepository.saveAll(Arrays.asList(parentA, parentB));
// fetch all parents
logger.info("---> 1st fetch all parents");
for (Parent parent : parentRepository.findAll()) {
logger.info(parent.toString());
}
// save a couple of children
Parent parentD = new Parent("Parent D"); Parent parentE = new Parent("Parent E"); Parent parentF = new Parent("Parent F");
Child childD = new Child("Child D", new HashSet<Parent>(Arrays.asList(parentD, parentE))); Child childE = new Child("Child E", new HashSet<Parent>(Arrays.asList(parentE, parentF)));
childRepository.saveAll(Arrays.asList(childD, childE));
// fetch all children
logger.info("---> 1st fetch all children");
for (Child child : childRepository.findAll()) {
logger.info(child.toString());
}
// fetch all parents
logger.info("---> 2nd fetch all parents");
for (Parent parent : parentRepository.findAll()) {
logger.info(parent.toString());
}
// fetch all children
logger.info("---> 2nd fetch all children");
for (Child child : childRepository.findAll()) {
logger.info(child.toString());
}
}
}
With JPA when you want to propagated an update to a relational item you have to specify the type of propagation you want to apply.
So when you define a relation ManyToMany you can add the cascade type "PERSIST" to propagate the instruction INSERT for the entity Parent
#ManyToMany(mappedBy = "children", cascade = CascadeType.PERSIST)
private Set<Parent> parents;
Here the complete specification for Hibernate used by Springboot
In case you are using annotatons you probably have noticed the cascade attribute taking an array of CascadeType as a value. The cascade concept in JPA is very is similar to the transitive persistence and cascading of operations as described above, but with slightly different semantics and cascading types:
CascadeType.PERSIST : cascades the persist (create) operation to
associated entities persist() is called or if the entity is managed
CascadeType.MERGE : cascades the merge operation to associated
entities if merge() is called or if the entity is managed
CascadeType.REMOVE : cascades the remove operation to associated
entities if delete() is called
CascadeType.REFRESH : cascades the
refresh operation to associated entities if refresh() is called
CascadeType.DETACH : cascades the detach operation to associated
entities if detach() is called CascadeType.ALL: all of the above

Hibernate - Transferring a Collection of objects from one parent object to another

I have 3 types of objects - Parent, Child, and ChildAttr
My goal is to transfer a subset of Children from one Parent to another using Hibernate (3.2.5).
Objects are structured as such:
public class Parent {
Set<Child> children;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "parent")
#Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
public Set<Child> getChildren() {
return this.children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
}
...
public class Child {
Set<ChildAttr> attributes;
Parent parent;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "child")
#Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
public Set<ChildAttr> getAttributes() {
return this.attributes;
}
public void setAttributes(Set<ChildAttr> attributes) {
this.attributes = attributes;
}
}
...
public class ChildAttr {
Child child;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "child_id", nullable = false)
public Child getChild() {
return this.child;
}
public void setChild(Child child) {
this.child = child;
}
}
Now say I run some client code that takes a subset of Parent A's Child objects and moves them to Parent B:
Set<Child> children = getChildrenToTransfer(transferCriteria, parentA);
parentA.getChildren().removeAll(children);
manager.saveOrUpdate(parentA); // method also calls flush();
parentB.getChildren().addAll(children);
manager.saveOrUpdate(parentB); // error thrown here.
I get an error thrown when trying to save parentB.
Found two representations of same collection: com.mycode.Child.attributes;
The application currently seems to do this work fine in multiple sessions - e.g. - some user comes along and removes the set of children, then some time later adds them to some other Parent. Moreover, I don't really understand why it's instantiating multiple versions of that attribute list when it should really just be one, even if the Parent changes.
What is causing the aforementioned error and how do I get around it?
One rule of thumb: ensure the objects are consistent as an object graph before trying to persist them... iow you're removing all the children from parent A and are adding them to parentB but you have not updated the parent link for those children.
So I'd suggest the following:
add methods to Parent:
add(Child child) {
child.setParent0(this);
children.add(child);
}
remove(Child child) {
child.setParent0(null);
children.remove(child);
}
And then on Child:
setParent0(Parent parent) {
this.parent = parent;
}
setParent(Parent parent) {
parent.add(this);
}
This way when you add from either direction you've got a consistent object model without the outside code knowing the details.
It makes more sense to remove from the parent...
So try with these methods.
Seens thats because your bidirectional relation (parent-children). When you remove/add the child to one parent to another, you should update the parent reference.

Hibernate Many-to-Many relation

I'm trying to implement Many-to-many relation using Hibernate and MySQL DB.
I have class User:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "users_nodes",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "node_id")})
private List<Node> nodeList;
and class Node:
#ManyToMany(mappedBy = "nodeList", cascade = CascadeType.ALL)
private List<User> users;
When I'm trying to save a new Node to DB(which already have user in it) it successfully add new entity to "nodes" table, but relation table "users_nodes" is empty.
This is the way I save Node entity:
#Override #Transactional
public void persist(Node entity) {
getCurrentSession().save(entity);
}
Thanks for your help!
You have to update the owner side of the association (User.nodeList), meaning that you have to associate the User with the Node for each associated user. For example:
class Node {
...
public void addUser(User user) {
if (!users.contains(user)) {
users.add(user);
user.addNode(this);
}
}
}
class User {
...
public void addNode(Node node) {
if (!nodeList.contains(node)) {
nodeList.add(node);
node.addUser(this);
}
}
}
If this would be a performance issue (if Users have many Nodes so it would be expensive to load them all when associating a new node with the desired users), you could change your mappings so that Node is the owner of the association and/or you could consider other options and improvements described here.

Eagerly fetch child when loading parent

I use Spring Data Jpa and Hibernate is the provider.
I have a Parent class mapped as follows:
#Entity
#Table(name="parent")
public class Parent {
private List<Child> childs;
private List<AnotherChild> anotherChilds;
#OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
public List<Child> getChilds() {
return childs;
}
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
public List<AnotherChild> getAntoherChilds() {
return anotherChilds;
}
}
and child:
#Entity
#Table(name="child")
public class Child {
private Parent parent;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "column_name")
public Parent getParent() {
return patern;
}
}
#Entity
#Table(name="another_child")
public class AnotherChild {
private Parent parent;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "column_name")
public Parent getParent() {
return patern;
}
}
When I load the parent from the database, it doesn't load the list of
child immediately and
When I call parent.getChilds(), it returns
null.
Can you give some advice? Am I wrong anywhere? Thanks.
EDIT:
After some research, I realize that when I have only single child, it loaded eagerly (like it should). But when I have multiple child, it doesn't - even though it has been marked FetchType.EAGER and the other FetchType.LAZY.
Note: If I marked both as FetchType.EAGER, it'll throws MultipleBagFetchException: cannot simultaneously fetch multiple bags.
The same happened when I annotate it using #Fetch(FetchMode.JOIN)
If added Entity annotation parent.getChilds() should not come empty.it would be better as you do Entity.
#Entity
#Table(name="PARENT_TBL")
public class Parent {
//other fields
#OneToMany(mappedBy = "parent",fetch = FetchType.LAZY,cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE},orphanRemoval = true)
private List<Child> childs;
//getter setter
}
#Entity
#Table(name="CHILD_TBL")
public class Child {
//other fields
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARENT_ID")
private Parent parent;
//getter setter
}
Example Get Parent Query;
public Parent getParent(long parentId) throws Exception {
session = sessionFactory.openSession();
Criteria cr = session.createCriteria(Parent.class, "parent");
cr.setFetchMode('parent.childs', FetchMode.JOIN);
cr.add( Restrictions.eq("parent.id", parentId));
Parent parent = cr.uniqueResult();
tx = session.getTransaction();
session.beginTransaction();
tx.commit();
return parent;
EAGER loading of collections means that they are fetched fully at the time their parent is fetched. So if you have Parent and it has List, all the childs are fetched from the database at the time the Parent is fetched.
LAZY on the other hand means that the contents of the List are fetched only when you try to access them. For example, by calling parent.getChilds().iterator(). Calling any access method on the List will initiate a call to the database to retrieve the elements. This is implemented by creating a Proxy around the List (or Set). So for your lazy collections, the concrete types are not ArrayList and HashSet.

JPA Cascade Delete with ManyToOne without OneToMany

I have the following structure of objects:
public class Entity1 {
#OneToMany(orphanRemoval=true)
private List<Entity2> listOfEntitys;
...
}
public class Entity2 {
#OneToMany(orphanRemoval=true)
private List<Entity3> listOfEntitys;
...
}
public class Entity3 {
...
}
//Here it can be thousand of it for one entity3
public class Entity4 {
#ManyToOne
private Entity3 entity;
...
}
Now, I want delete Entity1 and all entities should delete on cascade, too.
The Problem is,... how can I delete all entitys4 on cascade?
I have tried 2 solutions:
I add a list on entity3 and delete it on cascade.
I make two querys: first I select all entitys3 that could be in entity1 -> entity2 than I delete all entitys4 with it. After it I can delete entity1/2/3 normally per cascade because the entity reference from 4 to 3 is erased.
Give it another solution? Which solution should I prefer?
I would prefer the first solution but slightly modified:
#Entity
public class Entity1 {
#OneToMany(mappedBy = "entity1", orphanRemoval = true)
private List<Entity2> listofentitys;
}
#Entity
public class Entity2 {
#ManyToOne
private Entity1 entity1;
#OneToMany(mappedBy = "entity2", orphanRemoval = true)
private List<Entity3> listofentitys;
}
#Entity
public class Entity3 {
#ManyToOne
private Entity2 entity2;
#OneToMany(mappedBy="entity3", orphanRemoval = true)
private List<Entity4> listofentitys;
}
#Entity
public class Entity4 {
#ManyToOne
private Entity3 entity3;
}
By introducing bidirectional relationships between the entities you can avoid join tables thus improving overall performance while exchanging data with the underlying database. Such approach seems more reasonable to me than the second one as JPA implicitly takes care of everything.
Removing entity may look as follows:
public <T> void remove(Class<T> clazz, int id) {
T entity = em.find(clazz, id);
if (entity != null) {
em.remove(entity);
}
}
I hope it helps.

Categories

Resources