无法使用休眠删除将集合作为子项的实体

Posted

技术标签:

【中文标题】无法使用休眠删除将集合作为子项的实体【英文标题】:can not delete the entity which have collection as child with hibernate 【发布时间】:2011-03-29 11:24:53 【问题描述】:

注意:

这是一个交叉帖子:hibernate forum

由于我没有得到答复,所以我在这里询问是否可以得到一些帮助。 :)

如果这里解决了,我会在hibernate论坛上发布答案。 :)


在我的应用程序中,我有一些实体拥有一些集合,但我不知道要删除它们。

这是我的核心代码:

代码:

@Entity
@Table(
      name = "t_task")
public class Task 
   private int            id;
   private List<TaskStep>   steps   = new ArrayList<TaskStep>();

   public Task() 
      this.createTime = new Date();
   
   public Task(String name) 
      this();
      this.name = name;
   
   @Id
   @GeneratedValue
   public int getId() 
      return id;
   
   @OneToMany(
         cascade = CascadeType.ALL)
   @JoinColumn(
         name = "task_id",
         nullable = false)
   @LazyCollection(LazyCollectionOption.FALSE)
   @IndexColumn(
         name = "position")
   public List<TaskStep> getSteps() 
      return steps;
   
   // domain method
   public void addSteps(TaskStep ts) 
      steps.add(ts);
      ts.setTask(this);
   

   public void removeStep(TaskStep ts) 
      steps.remove(ts);
   

   // setter
   public void setId(int id) 
      this.id = id;
   

   public void setSteps(List<TaskStep> steps) 
      this.steps = steps;
      for (TaskStep st : steps) 
         st.setTask(this);
      
   




//TaskStep:

@Entity
@Table(
      name = "t_taskstep")
public class TaskStep 
   private int            id;
   private List<Operator>   operator   = new ArrayList<Operator>();
   private Task         task;

   public TaskStep() 

   @Id
   @GeneratedValue
   public int getId() 
      return id;
   

   @ManyToMany(
         cascade = CascadeType.ALL)
   @LazyCollection(LazyCollectionOption.FALSE)
   public List<Operator> getOperator() 
      return operator;
   

   @ManyToOne
   @JoinColumn(
         name = "task_id",
         nullable = false,
         updatable = false,
         insertable = false)
   public Task getTask() 
      return task;
   
   // domain method start
   public void addOperator(Operator op) 
      operator.add(op);
   
   // setter
   public void setId(int id) 
      this.id = id;
   

   public void setOperator(List<Operator> operator) 
      this.operator = operator;
   

   public void setTask(Task task) 
      this.task = task;
   


//Operator:
@Entity
@Table(
      name = "t_operator")
public class Operator 
   private int            id;
   private List<TaskStep>   steps   = new ArrayList<TaskStep>();

   public Operator() 
   @Id
   @GeneratedValue
   public int getId() 
      return id;
   
   // //setter
   public void setId(int id) 
      this.id = id;
   

   public void setSteps(List<TaskStep> steps) 
      this.steps = steps;
   
   @ManyToMany(
         mappedBy = "operator")
   public List<TaskStep> getSteps() 
      return steps;
   

在db中,有"t_task","t_operator","t_step","t_step_operator"的表。

我使用这种方式来移除任务: 代码: taskDao.delTask​​ById(5);

这是道:

代码:

   public void delTaskById(int id) 
      Task t = queryTaskById(id);
      Session sess = factory.getCurrentSession();
      try 
         sess.beginTransaction();
         sess.delete(t); // 1)
         sess.flush();
         sess.getTransaction().commit();
       catch (HibernateException e) 
         sess.getTransaction().rollback();
      
   

我收到一条错误消息,提示“无法删除或更新父行......”。

然后我尝试使用repalce

sess.delete(t)

sess.createQuery("delete from Task t where t.id="+id).executeUpdate()

我现在收到错误,但实际上并没有删除任务。

我已经在映射中设置了级联。例如,对于任务对象中的tasksteps,我设置cascade.type=all,所以我认为当hibernate尝试删除任务时,它也应该删除它的realted taskssteps,当它尝试删除一个taskstep对象时,它会发现“t_step_t_operator”表引用了“t_step”中的id,所以我还在任务类的“step”字段中设置了“cascade=all”。 但似乎发生的事情并不是我想的那样......

有什么问题?我要疯了!

谁能帮我一个忙?

BWT,级联是什么意思?

例如: 在 TaskStep 类中,我有一个运算符列表;

   //Class:TaskStep.
   @ManyToMany(
         cascade = CascadeType.ALL)
   @LazyCollection(LazyCollectionOption.FALSE)
   public List<Operator> getOperator() 
      return operator;
   

在上面的例子中,我设置了cascade = all,这是否意味着无论对TaskStep进行什么操作(curd),它都会对Opeartors进行相同的操作?

【问题讨论】:

【参考方案1】:

如果你想(模拟)一个没有连接表的双向关系,你必须通过创建两个单向关系来做到这一点,并通知 Hibernate 它们之间的关系——正如 JB Nizet 所说,通过使用 mappedBy,但同时保留另一个(@ManyToOne) 映射

@JoinColumn 可以使用,例如,如果您只想映射关系的 @OneToMay 侧但仍然不想要连接表 - 您将指出其他表中应该使用的列,通常是单向引用不影响引用的表,而是使用连接表。在这种情况下你可能不需要@JoinColumn,因为默认情况下 PK (id) 用于双向关系中的 FK 约束(t_taskstep.task_id 列将对 t_task.id 具有 FK 约束)

Cascade 属性将导致对拥有实体的创建/删除操作(更新由 EntityManager/Session 隐式处理)级联到(在您的情况下)集合中的所有实体。因此,对 Task 执行删除操作也会导致删除步骤集合中的所有 TaskSteps 实例,并且还会删除 TaskStep 中的 Operator 运算符。勇敢,但可能会失败,因为您涉及 @ManyToMany 关系 - 其他 TaskStep 实例可能会引用您的删除级联到的 Operator。通常只级联保存更新(或在组件/嵌入式映射关系)并手动处理删除。如果您确定您的集合是唯一一个引用另一个实体的集合,您可以使用 ALL 和 DELETE-ORPHAN,因此只需从集合中删除引用的实体即可删除它们。

@IndexColumn 用于如果您希望记住 List 中对象的顺序。如果您不在乎,请跳过它,您的关系将具有 Bag 语义(无序,允许重复)。通常您不希望在 Collection 中看到重复项,因此应该使用 Set Collection。

回答有点长,抱歉。也许应该简单地提供代码。

【讨论】:

我测试过,如果我只是在映射中使用mappedBy,是不会设置位置的。所以我必须定义两次“joinColumn”,看看这个:opensource.atlassian.com/projects/hibernate/browse/HHH-4390。顺便说一句,从您的友好回答中,我对双向关系和双向引用感到困惑。在我的例子中,任务和任务步骤都持有一个引用,这是双向引用,但是这个适合的关系是什么? bi 还是 un?【参考方案2】:

Task 和 TaskStep 之间的关系映射了两次:一次在 Task 中,一次在 TaskStep 中。 Task 中的@JoinColumn 应该消失,@OneToMany 应该有一个“mappedBy”属性:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "task")
@LazyCollection(LazyCollectionOption.FALSE)
@IndexColumn(name = "position")
public List<TaskStep> getSteps() 
    return steps;

【讨论】:

是的,你是对的,但是如果我只设置一次关系,taskstep中的位置列就不能正常工作,所以我设置了两次。(一个任务的tasksteps的顺序是在我的应用程序中需要。您能告诉我有关级联的信息吗?我无法删除任务。 我怀疑在添加 mappedBy 时位置管理无法正常工作,因为在向任务添加步骤时忘记初​​始化反向关系。您有责任确保双方都进行了修改。见***.com/questions/5460573/…。关系应始终映射一次,而不是两次。 感谢您的关注。在我的任务中,当我添加一个任务步骤时,我将更新任务步骤的引用任务,就像:“public void addSteps(TaskStep ts) steps.add(ts); ts.setTask(this); ” 所以,我想我已经开始了这种关系。顺便说一句,这是我无法删除任务对象的原因吗?从昨天开始,当我没有在taskStep中添加“indexColum”时,我也无法删除它。

以上是关于无法使用休眠删除将集合作为子项的实体的主要内容,如果未能解决你的问题,请参考以下文章

无法使用休眠 OGM 从 mongoDB 中删除元素

删除嵌套集合中的条目

休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话

使用 JPQL 连接不相关的实体在休眠 5.3.2 中无法在 Spring 数据中工作

实体上的休眠预设ID,无法使其持续存在

无法在休眠状态下保存实体