Java 更新父对象时休眠不更新子对象

Java 更新父对象时休眠不更新子对象,java,hibernate,jpa,orm,annotations,Java,Hibernate,Jpa,Orm,Annotations,我有两张桌子,父母和孩子之间有@oneTomany关系。 下面是我的表结构 表格结构 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, `nam

我有两张桌子,父母和孩子之间有@oneTomany关系。 下面是我的表结构

表格结构

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`)
)
@Entity
@Table(name = "parent")
public class Parent {

    .....   

    @OneToMany(cascade = CascadeType.PERSIST,mappedBy = "defaultchild", fetch = FetchType.EAGER)
    private Set<Child> childs;

    //setter and getters.
}

@Entity
@Table (name = "child")
public class Child {

    ....

    @ManyToOne
    @JoinColumn(name="parent_id")
    private Child defaultchild; 

    //Setter and Getter methods
}
我已经为此创建了实体类,如下所示

父类。

@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.
}
@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
}
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;
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void moveAllChildren(String srcName, String dstName) {
    DAO.moveAllChildren(srcName, dstName);
} 
DAO类。

@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.
}
@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
}
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;
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void moveAllChildren(String srcName, String dstName) {
    DAO.moveAllChildren(srcName, dstName);
} 
但当我试图调用以下代码时

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);
这意味着我的更新代码不能正常工作,下面是服务日志。updateParent(otherParent)语句

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
请帮帮我我不知道这个代码出了什么问题
提前感谢。

如果没有具体原因限制您的
@Cascade
类型,您可以选择切换到ALL

  @Cascade({ CascadeType.ALL})
甚至只是

  @OneToMany(cascade=CascadeType.ALL, mappedBy="defaultchild")

编辑:您有几次拼错了“parent”,修复它也不会有什么坏处。

您正试图删除
parent
,但它仍然可能有
childs
。在删除之前,请尝试清空
childs
集合:

for(Child child : parent.getChilds()){
    child.setParent(null);
}
parent.getChilds().clear();
Service.deleteParent(parent);

如果
子对象上仍有引用,则不能删除
父对象

我可能只编写一个DAO方法moveAllChildren(String srcName,String dstName),如下所示:

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);
    }
}
公共类ParentDAO{
@持久上下文
私人实体管理者;
公共父级findParentByName(名称){
TypedQuery q=em.createQuery(“从父级中选择p,其中p.name=:name”,Parent.class);
返回q.setParameter(“name”,name).getSingleResult();
}
public void moveAllChildren(字符串srcName、字符串dstName){
父src=findParentByName(srcName);
父dst=findParentByName(dstName);
Set children=newhashset();
for(子c:src.getChildren()){
添加(c);
}
src.getChildren().removeAll(children);
dst.getChildren().addAll(子对象);
}
}
通常,在使用级联时,最好明确地添加和删除子级,而不是说dst.setChildren(allChildren),这样JPA就有机会管理关系的双方。如果您不这样做,您的孩子可能仍然认为src是他们的父母,然后您可能会看到数据库中的约束冲突


此外,让JPA管理尽可能多的实体内容也是一个好主意。因此,我不希望应用程序调用服务调用DAO来检索父对象,而只是移动它们的子对象,然后调用相同的服务调用相同的DAO来将这些更改合并到数据库中。您最好在DAO级别将其实现为一个低级操作,然后添加一个调用该操作的服务来处理@Transaction。

最后,在wallenborn和下面的链接的帮助下,我得到了解决方案

以下是我在课堂上所做的改变

实体类。

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`)
)
@Entity
@Table(name = "parent")
public class Parent {

    .....   

    @OneToMany(cascade = CascadeType.PERSIST,mappedBy = "defaultchild", fetch = FetchType.EAGER)
    private Set<Child> childs;

    //setter and getters.
}

@Entity
@Table (name = "child")
public class Child {

    ....

    @ManyToOne
    @JoinColumn(name="parent_id")
    private Child defaultchild; 

    //Setter and Getter methods
}
DAO类

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);
}
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);
}
公共父级findParentByName(名称){
TypedQuery q=em.createQuery(“从父级中选择p,其中p.name=:name”,Parent.class);
返回q.setParameter(“name”,name).getSingleResult();
}
public void moveAllChildren(字符串srcName、字符串dstName){
父src=findParentByName(srcName);
父dst=findParentByName(dstName);
Set children=newhashset();
for(子c:src.getChildren()){
添加(c);
}
src.getChildren().removeAll(children);
dst.getChildren().addAll(子对象);
}

感谢您的快速回复。我确实尝试了您的代码,但当我使用@OneToMany(cascade=CascadeType.ALL,mappedBy=“defaultchild”)或@cascade({CascadeType.ALL})时,子条目被删除了事实上,我只是通过更改实体的名称给出了我的问题的一个场景,这就是为什么会出现这种拼写错误。感谢您的回复,我尝试了上面给出的代码,但它仍然给了我上面的错误:无法删除或更新父行:外键约束失败消息告诉我仍然存在错误父项上的参考
。试着在数据库中查找。您的代码可以与两个父项一起工作:otherParent和Parent。试着把这两个电话分开,尽量简单。在for循环中,我做了一些更改,就像更新每个子实体一样,也像child.setParent(otherParent);我检查了日志,它给出了更新查询,但当我签入数据库时,条目仍然没有更新。这是JPA还是Hibernate?我这样问是因为您有一个带有@Transactional注释的JPAish服务层,但是您的DAO似乎自己管理事务。这是故意的吗?@welleborn以前我只在DAO层使用事务,但它不会对我的数据库进行任何更改,阅读一篇文章后,我发现我必须使用@Transactional,这就是我在这里使用@Transactional的原因。我尝试了上面给出的代码,将所有子项从src移动到dst,并更新两个父项。现在我得到org.hibernate.ObjectDeletedException:已删除的对象将通过级联重新保存(从关联中删除已删除的对象)错误,当我从@ManyToOne中删除@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,org.hibernate.annotations.CascadeType.MERGE})时,我收到的错误与上面提到的外键约束失败相同。您可以选择。您可以让DAO方法开始并提交事务,也可以将服务方法标记为@Transactional,并让容器管理器处理细节。后一种方法是imo首选的,因为它允许您将DAO方法用作构建块,而不必担心它们会干扰彼此的事务。但不要两者都用。选一个。你不应该更新父母。只要在服务层中有一个'@Transactional'方法,让DAO使用'@PersistenceContext'来检索父对象,移动子对象,然后让事务提交。剩下的由Hibernate负责。谢谢