Why does Hibernate try to delete when I try to update/insert?

后端 未结 4 587
独厮守ぢ
独厮守ぢ 2020-12-08 01:44

In my app I have these Hibernate-mapped types (general case):

class RoleRule {
  private Role role;
  private PermissionAwareEntity entity; // hibernate-mapp         


        
相关标签:
4条回答
  • 2020-12-08 01:59

    See the answer of the question 'Overriding equals and hashCode in Java'.

    It explains how to override the equals and hashCode methods, which seemed to be your problem as it worked after having them rewritten.

    Wrongly overriding them can lead hibernate to delete your collections and reinsert them. (as the hash key is used as keys in maps)

    0 讨论(0)
  • 2020-12-08 02:02

    I'm not sure if this is the solution, but you might want to try:

    @OneToMany(mappedBy = "role")
    

    And not have the @JoinColumn annotation? I think both entities are trying to 'own' the association, which is why the SQL might be messed up?

    Also, if you want to ensure only affected columns get updated, you can use a hibernate-specific annotation on the class:

    @Entity
    @org.hibernate.annotations.Entity(
        dynamicInsert = true, dynamicUpdate = true
    )
    
    0 讨论(0)
  • 2020-12-08 02:09

    I've generally used two methods of updating a collection (the many side of a one-to-many) in Hibernate. The brute force way is to clear the collection, call save on the parent, and then call flush. Then add all of the collection members back and call save on the parent again. This will delete all and then insert all. The flush in the middle is key because it forces the delete to happen before the insert. It's probably best to only use this method on small collections since it does re-insert all of them.

    The second way is harder to code, but more efficient. You loop through the new set of children and manually modify the ones that are still there, delete the ones that aren't, and then add the new ones. In pseudo-code that would be:

    copy the list of existing records to a list_to_delete
    for each record from the form
       remove it from the list_to_delete
       if the record exists (based on equals()?  key?)
         change each field that the user can enter
       else if the record doesn't exist
         add it to the collection
    end for
    for each list_to_delete
      remove it
    end for
    save
    

    I've searched the Hibernate forums for many hours trying to find the right way to solve this problem. You should be able to just update your collection to make it accurate and then save the parent, but as you've found out, Hibernate tries to detach the children from the parent before deleting them and if the foreign key is not-null, it will fail.

    0 讨论(0)
  • 2020-12-08 02:15

    Brian Deterling's answer helped me to overcome the phantom delete. I wish he had put a real code. Here is what I got from his suggestion 1. Posting for someone to use it or to comment my code.

    // snFile and task share many to many relationship
    
    @PersistenceContext
    private EntityManager em;
    
    public SnFile merge(SnFile snFile) {
            log.debug("Request to merge SnFile : {}", snFile);
    
            Set<Task> tasks = taskService.findBySnFilesId(snFile.getId());
            if(snFile.getTasks() != null) {
                snFile.getTasks().clear();
            }
            em.merge(snFile);
            em.flush();
            if(tasks != null) {
                if(snFile.getTasks() != null)
                    snFile.getTasks().addAll(tasks);
                else
                    snFile.setTasks(tasks);
            }
    
            return em.merge(snFile);
        }
    
    0 讨论(0)
提交回复
热议问题