Hibernate not updating child object while updating parent object
annotations, hibernate, java, jpa, orm
Solution
I would probably just write a DAO method moveAllChildren(String srcName, String dstName), like this:
public class ParentDAO {
@PersistenceContext
private EntityManager em;
public Parent findParentByName(name) {
TypedQuery<Parent> q = em.createQuery("select p from Parent where p.name = :name", Parent.class);
return q.setParameter("name", name).getSingleResult();
}
public void moveAllChildren(String srcName, String dstName) {
Parent src = findParentByName(srcName);
Parent dst = findParentByName(dstName);
Set<Child> children = new HashSet<Child>();
for (Child c: src.getChildren()) {
children.add(c);
}
src.getChildren().removeAll(children);
dst.getChildren().addAll(children);
}
}
In General, when using Cascading, it is Good Practice to add and remove the children explicitly, rather than to say dst.setChildren(allChildren), to give JPA a chance to manage both sides of the Relation. If you don't do that, you risk that a child still thinks src is their parent, and then you'll likely to see a constraint violation from your database.
Additionally, it is a Good Idea to try to let JPA manage as much of your entity stuff as possible. So i'd rather not have the application call a service call a DAO to retrieve the parents only to move their children and then call the same service call the same DAO to merge these changes into the database. You're better off implementing this as a low-level operation on the DAO level and then add a service that calls that to handle the @Transaction.
Problem
I have two tables parent and child has @oneTomany relationship between them. following is my table structure. Table Structure ``` CREATE TABLE `parent` ( `id_parent` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id_parent`) ) CREATE TABLE `child` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `parent_id` int(3) NOT NULL, PRIMARY KEY (`id`), KEY `fk_parent_child` (`group_id`), CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id_parent`) ) ``` I have created Entity classes for this as follows Parent Class. ``` @Entity @Table(name = "parent") public class Parent { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id_parent") private int id; @Column(name = "name") private String name; @OneToMany(mappedBy = "defaultchild", fetch = FetchType.EAGER) @Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE }) private Set<Child> childs; //setter and getters. } ``` Child Class. ``` @Entity @Table (name = "child") public class Report { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column (name = "id") private int id; @Column (name = "name") private String name; @ManyToOne @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,org.hibernate.annotations.CascadeType.MERGE}) @JoinColumn(name="parent_id") private Parent defaultchild; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "group",joinColumns={@JoinColumn(name="fk_child_id")},inverseJoinColumns={@JoinColumn(name="fk_group_id")}) private Set<XXX> groups = new HashSet<XXX>(0); //Setter and Getter methods } ``` Service Class ``` public UIGroup getParentByName(String name) { return DAO.getParentByName(name); } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public boolean updateParent(Parent parent) { return DAO.updateParent(parent); } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public boolean deleteParent(Parent parent) { return DAO.deleteParent(parent); } ``` DAO Class. ``` public UIGroup getParentByName(String name) { Query query; Parent parent = null; try { String queryString = " from Parent where name = :name"; query = sessionFactory.getCurrentSession().createQuery(queryString); query.setParameter("name", name); uiGroup = (Parent) query.uniqueResult(); } catch (Exception e) { logger.error(e); } return parent; } public boolean updateParent(Parent parent) { boolean result = true; Session session = null; Transaction tx = null; try { session = sessionFactory.getCurrentSession(); tx = session.beginTransaction(); session.merge(parent); tx.commit(); session.flush(); } catch (HibernateException e) { result = false; logger.error(e); }// end of try-catch block. return result; } public boolean deleteParent(Parent parent) { boolean result = true; Session session = null; try { session = sessionFactory.getCurrentSession(); session.delete(parent); } catch (HibernateException e) { result = false; logger.error( + e); } return result; } ``` but when I am trying to invoke following code ``` Parent otherParent = Service.getParentByName("Other"); Parent parent = Service.getParentByName("XYZ"); //here I am assigning childs assign to XYX parent to other Parent Set<Child> childs = new TreeSet<Child>(Child.COMPARE_BY_ID); childs.addAll(otherParent.getchildes()); childs.addAll(parent.getchilde()); otherParent.setChilds(childs); Service.updateParent(otherParent); Service.deleteParent(parent); ``` I am getting following error. Error ``` java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`database`.`child`, CONSTRAINT `fk_parent_child` FOREIGN KEY (`parent_id`) REFERENCES `child` (`id_parent`)) ``` which means my update code is not working properly following is log of Service.updateParent(otherParent) statement ``` SELECT parent0_.id_parent AS id1_135_1_, parent0_.name AS name135_1_, childs1_.parent_id AS parent4_3_, childs1_.id AS id3_, childs1_.id AS id143_0_, childs1_.child_name AS child2_143_0_, childs1_.is_sea_child AS is3_143_0_, childs1_.parent_id AS parent4_143_0_ FROM ui_parent parent0_ LEFT OUTER JOIN child childs1_ ON parent0_.id_ui_parent=childs1_.parent_id WHERE parent0_.id_parent=1 ``` please help me I don't know what went wrong with this code thanks in advance.