These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: OneToMany and dirty checking of the set
PostPosted: Sat May 02, 2009 7:03 pm 
Newbie

Joined: Wed Jul 30, 2008 1:03 am
Posts: 16
Hi,

I have two entities:

Code:
@Entity
Class A {

  @OneToMany (cascade = {CascadeType.PERSIST, CascadeType.MERGE},
      fetch = FetchType.LAZY
  )
  @JoinColumn (name = "a_id")
  @org.hibernate.annotations.Cascade({
      org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
      org.hibernate.annotations.CascadeType.SAVE_UPDATE
  })
  private Set<B> setOfB;

  public void addB(B b) {
    setOfB.add(b);
  }

}

@Entity
Class B {
  ...
}


Now, lets say:

Code:
A a = new A();
B b1 = new B();
a.addB(b1);


now I persist "a". I see one entry in table A and one entry in table B. So far so good.

Now at a later point, I fetch "a" through a get(). What I have is the previous snapshot. Then what I do is:

Code:
B b2 = new B();
a.addB(b2);


At this point, when I saveOrUpdate(a), I do see that "b1" gets DELETED and "b1" and "b2" get INSERTED. Why is this happening? I had expected to see one INSERT for "b2" with no impact on "b1".

Am I doing something wrong? How can I solve this problem?

Appreciate the help.
Ramin


Top
 Profile  
 
 Post subject: Re: OneToMany and dirty checking of the set
PostPosted: Tue May 05, 2009 5:29 am 
Regular
Regular

Joined: Thu Sep 06, 2007 2:22 am
Posts: 108
Location: Noida,India
I don't see any problem in code or mapping. i think you are missing something to mention.


Top
 Profile  
 
 Post subject: Re: OneToMany and dirty checking of the set
PostPosted: Tue May 05, 2009 8:01 pm 
Newbie

Joined: Wed Jul 30, 2008 1:03 am
Posts: 16
Thanks.

You are right in that I simplified the example. So are you saying that the correct behavior is what I'm expecting? What would the cause of the complete drop and recreate of the set be? For example, does it matter that the Set is being initialized in the constructor?


Top
 Profile  
 
 Post subject: Re: OneToMany and dirty checking of the set
PostPosted: Wed May 06, 2009 12:31 am 
Regular
Regular

Joined: Thu Sep 06, 2007 2:22 am
Posts: 108
Location: Noida,India
Please post the code where you are exactly facing problem, Because what query will execute depends upon how you are manipulating the set.(assuming mapping is correct).


Top
 Profile  
 
 Post subject: Re: OneToMany and dirty checking of the set
PostPosted: Wed May 06, 2009 5:27 pm 
Newbie

Joined: Wed Jul 30, 2008 1:03 am
Posts: 16
Here is my code. Subscriber has a list of DeviceLine. DeviceLine has ref to actual Device. SubscriberService is business logic layer to save subscribers. Please let me know where the problem could be. We tried merge() in modifySubscriber() but still the same problem.

Code:
package com.mycomany.test;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "subscriber")
public class Subscriber {

  /**
   * Internal database id.
   */
  @Id
  @Column(name = "id", nullable = false)
  protected Long id;
 
  protected String resourceId;

  protected String name;

  @OneToMany (cascade = {CascadeType.PERSIST, CascadeType.MERGE},
      fetch = FetchType.LAZY
  )
  @JoinColumn (name = "subscriber_id")
  @org.hibernate.annotations.Cascade({
      org.hibernate.annotations.CascadeType.DELETE_ORPHAN,
      org.hibernate.annotations.CascadeType.SAVE_UPDATE
  })
  private Set<DeviceLine> listOfDevices;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public Set<DeviceLine> getListOfDevices() {
    return listOfDevices;
  }

  public void setListOfDevices(Set<DeviceLine> listOfDevices) {
    this.listOfDevices = listOfDevices;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getResourceId() {
    return resourceId;
  }

  public void setResourceId(String resourceId) {
    this.resourceId = resourceId;
  }

}//end class



Code:
package com.mycomany.test;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "device_line")
public class DeviceLine {

  /**
   * Internal database id.
   */
  @Id
  @Column(name = "id", nullable = false)
  protected Long id;

  protected String name;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "device_id")
  @org.hibernate.annotations.Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE })
  private Device device;

  public boolean equals(Object o) {
 
    DeviceLine d = (DeviceLine)o;
   
    if(this.getDevice().getResourceId().equals(d.getDevice().getResourceId())
        && this.getName().equals(dl.getName())) {
      return true;
    }
   
    return false;
 
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public Device getDevice() {
    return device;
  }

  public void setDevice(Device device) {
    this.device = device;
  }
 
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
 
}


Code:
package com.mycomany.test;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;



@Entity
@Table(name = "device")
public class Device {

  /**
   * Internal database id.
   */
  @Id
  @Column(name = "id", nullable = false)
  protected Long id;
 
  protected String name;
 
  protected String resourceId;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getResourceId() {
    return resourceId;
  }

  public void setResourceId(String resourceId) {
    this.resourceId = resourceId;
  }

}


Code:
package com.mycomany.test;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import vzbi.cssop.common.util.Util;

public final class SubscriberService {

  @Transactional(propagation=Propagation.REQUIRED)
  public void createSubscriber(Subscriber subscriber) {

    Subscriber sub = null;
    Device dev = null;

    try {


      Set<DeviceLine> device_lines = subscriber.getListOfDevices();
      for (DeviceLine d : device_lines) {
        dev = daoManager.getDeviceDao().getByResourceId(d.getDevice().getResourceId());
        if(dev != null) {
          d.setDevice(dev);
        }
      }

      daoManager.getSubscriberDao().save(subscriber);

    } catch (Throwable e) {
    } finally {
    }

    return;

  }

  @Transactional(propagation=Propagation.REQUIRED)
  public void modifySubscriber(Subscriber subscriber) {

    Subscriber sub = null;

    try {

      //load sub from database
      sub = daoManager.getSubscriberDao().getByResourceId(subscriber.getResourceId());
      if (sub == null) {
        throw new RuntimeException("Subscriber not found!");
      }

      sub.setName(subscriber.getName());


      Set<DeviceLine> device_lines = subscriber.getListOfDevices();
      for (DeviceLine d : device_lines) {
        if(d.getDevice().getId() == null ) {
          Device dev = daoManager.getDeviceDao().getByResourceId(d.getDevice().getResourceId());
          if(dev != null) {
            d.setDevice(dev);
          }
        }
      }

      updateSet(sub.getListOfDevices(), subscriber.getListOfDevices());

      daoManager.getSubscriberDao().save(sub);

    } catch (Throwable e) {
    } finally {
    }

    return;

  }

  public static <T> void updateSet(Set<T> currentSet, Set<T> newSet) {
   
    Iterator<T> it = currentSet.iterator();
    while(it.hasNext()) {
      if(!newSet.contains(it.next())) {
        it.remove();
      }
    }
   
    for (T t : newSet) {
      if(!currentSet.contains(t)) {
        currentSet.add(t);
      }
    }
   
  }//end updateSet()

}//end class


Thanks
Ramin


Top
 Profile  
 
 Post subject: Re: OneToMany and dirty checking of the set
PostPosted: Tue May 12, 2009 11:24 am 
Newbie

Joined: Wed Jul 30, 2008 1:03 am
Posts: 16
One more thing that might be useful for the answer is that the equal and hashCode we defined for the items of the Set are not being called at all. This is a surprise to me since an addition to the set must call these, doesn't it?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.