Java 更新父对象时休眠不更新子对象
我有两张桌子,父母和孩子之间有@oneTomany关系。 下面是我的表结构 表格结构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
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负责。谢谢