-->
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.  [ 9 posts ] 
Author Message
 Post subject: JPA remove/refresh with associated objects.. how to do it?
PostPosted: Thu Nov 29, 2007 4:16 am 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
Hi,

We are using the Hibernate JPA implementation and things are going ok.
Hibernate core 3.2.5, annotations 3.3.0, entity manager 3.3.1.

We have a web/jboss based front end that does the db updates via JPA and a separate JVM that uses the data. When things change we use RMI to send the other JVM a kick and it does a refresh.

We have a template and a set of instruments linked to a template.

When we add an instrument to a template, the other JVM does a refresh of the template, finds the new instrument and works ok.

When we delete an instrument from a template, the other JVM does a refresh, but gets an entity not found exception on the instrument that was deleted.

We do mix attribute and method based annotations in different classes - could that be an issue?

Here are bits of the classes:

@Entity
@Table(name = "RB_QM_TEMPLATE")
@javax.persistence.SequenceGenerator(
name="RB_QM_TEMPLATE_SEQ",
sequenceName="RB_QM_TEMPLATE_SEQ")
public class Template {

...

@OneToMany(mappedBy = "template", cascade = {javax.persistence.CascadeType.ALL})
@Cascade({org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
@JoinColumn(name = "qm_template_id")
private Set<InstrumentTemplate> instruments;

...
}

@Entity
@BatchSize(size=128)
@Table(name = "RB_QM_INST")
public class InstrumentTemplate {

...

@ManyToOne
@JoinColumn(name = "qm_template_id")
public Template getTemplate() {
return template;
}
...
}

Thanks in advance for any tips.
Chris


Last edited by kimptoc on Mon Feb 11, 2008 1:09 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 29, 2007 1:54 pm 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
The workaround we have currently is to manually remove the instrument from the template collection before doing the refresh. This seems to work - we get new stuff and hibernate is not upset by things that have been deleted.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 11, 2008 1:07 pm 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
hmmm... this solution is breaking down for more complex bits of the data model with 4 or 5 levels of objects.

I am going to debug it in detail tomorrow, but the AbstractEntityManagerImpl refresh within the entitymanager world seems to be doing the refresh - but perhaps something higher up in the chain in the core hibernate world needs to be configured to go back to the DB because objects may have been deleted. The code assumes that if refresh is called on it then it must still exist, so I guess the parent object needs to invalidate its children before doing the refresh.

I guess this is outside of the JPA standard and is handled by a hibernate extension... hopefully :)

Regards,
Chris


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 12, 2008 7:09 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
I don't get it. Where is the unexpected behaviour?
You delete an entity, of course your other VM can't find that entity anymore!?

I'm confused

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 12, 2008 11:32 am 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
Sorry for not being clear.

One VM is doing the maintenance, hence deletes the entity.

The other VM wants to reload the objects - so I do a refresh - but I was hoping that the refresh would quietly handle that some objects have been removed and so remove them from the local collections.

Maybe an example would help:

if you have an Order object with an OrderLines collection. In one VM we might remove some OrderLine entries. In the other VM, when I refresh the Order I am looking to just see the revised set of OrderLines, but instead hibernate wants to reload the deleted OrderLine and then gives an error.

If the Order was deleted, I would expect to get an error - but since it is the owner of the OrderLines, I am hoping I can refresh it and get what I want.

I am obviously using hibernate in the wrong way.

Perhaps I need to "evict" the Order before refreshing it? Although it looks like I need the hibernate session in order to do this but I can't seem to find a way to get it :(

Thanks,
Chris


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 12, 2008 11:48 am 
Senior
Senior

Joined: Tue Jul 25, 2006 9:05 am
Posts: 163
Location: Stuttgart/Karlsruhe, Germany
Hi,

You should be to get the hibernate session by calling entityManager.getDelegate();. This normally should return the underlying hibernate session. It is also worth noting that depending on the environment (JBoss 4.0.4/4.0.5) the entityManager.getDelegate() call can return an EntityManagerImpl object. If an EntityManagerImpl is returned calling getDelegate() on this will return the hibernate session.

Cheers,

Andy

_________________
Rules are only there to be broken


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 12, 2008 12:21 pm 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
Thanks - got the hibernate session now and doing the evict before the refresh avoids the "not found" problem - but still does not seem the most elegant solution. Ideally it should just work, but the next best is to evict the OrderLines - but doing evict on the individual items is too low down - the objects are still in the Order's collection.

Looking the hibernate code (admittedly, core hibernate), the DefaultRefreshEventListener.onRefresh method has a call to evictCachedCollections but after the refresh has been cascaded to its children. It seems like this should happen before the refresh is cascaded, or at least be an option.

Is there a way to configure hibernate/annotate my code to trigger this kind of behaviour?

Thanks,
Chris


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 4:46 am 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
Ok - this is confusing now, I can extend the hibernate tests and it does what I want, but the JPA version does not...

Note now using hibernate-3.2.6.

I get this exception:

Quote:
No row with the given identifier exists: [org.hibernate.ejb.test.cascade.Soldier#1]
javax.persistence.EntityNotFoundException: No row with the given identifier exists: [org.hibernate.ejb.test.cascade.Soldier#1]
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
at org.hibernate.ejb.AbstractEntityManagerImpl.refresh(AbstractEntityManagerImpl.java:269)
at org.hibernate.ejb.test.cascade.FetchTest.testDeleteRefreshProblem(FetchTest.java:63)


Core hibernate test, added to DeleteTest class:
Code:
    public void testRefreshAfterDeleteInOtherSession() {
      Session s = openSession();
        s.beginTransaction();
      Node parent = new Node( "parent" );
      Node child = new Node( "child" );
      parent.getCascadingChildren().add( child );
        assertEquals("s parent has 1 child", 1, parent.getCascadingChildren().size());
      s.persist( parent );
      s.getTransaction().commit();

        deleteChildViaOtherSession("parent");

        s.refresh(parent);
       
        assertEquals("s parent has no children", 0, parent.getCascadingChildren().size());
    }

    private Node deleteChildViaOtherSession(String parentId) {
        Node parent;
        Session s2 = openSession();
        s2.beginTransaction();
        parent = ( Node ) s2.get( Node.class, parentId);
        for (Iterator iterator = parent.getCascadingChildren().iterator(); iterator.hasNext();) {
            Object o = iterator.next();
            iterator.remove();
        }
        s2.getTransaction().commit();
        s2.close();
        return parent;
    }


And its hbm file (unchanged from the distribution):

Code:
   <class name="Node" polymorphism="explicit">
      <id name="name">
         <generator class="assigned"/>
      </id>
      <property name="description"/>
      <many-to-one name="parent"/>
      <property name="created" not-null="true"/>
      <set name="children"
         inverse="true"
         cascade="persist,merge,save-update,evict">
         <key column="parent"/>
         <one-to-many class="Node"/>
      </set>
      <set name="cascadingChildren" inverse="false" cascade="persist,merge,save-update,evict,delete">
            <key column="CASC_PARENT"/>
            <one-to-many class="Node"/>
        </set>
    </class>


Here is the JPA version, added to the FetchTest:

Code:
    public void testDeleteRefreshProblem() throws Exception {

        EntityManager em = factory.createEntityManager();
        em.getTransaction().begin();
        Troop disney = new Troop();
        disney.setName( "Disney" );
        Soldier mickey = new Soldier();
        mickey.setName( "Mickey" );
        disney.addSoldier( mickey );
        em.persist( disney );
        em.getTransaction().commit();

        removeMickey(disney.getId());

        em.refresh(disney);
       
        assertEquals("Check no soldiers", 0, disney.getSoldiers().size());
    }

    private void removeMickey(Integer id) {
        EntityManager em = factory.createEntityManager();
        em.getTransaction().begin();
        Troop disney = em.find( Troop.class, id );
        disney.getSoldiers().clear();
        em.getTransaction().commit();
        em.close();
    }



Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 13, 2008 6:24 am 
Beginner
Beginner

Joined: Tue Jun 27, 2006 6:14 am
Posts: 24
The difference seems to be that the hibernate core/hbm versions gets the child object being returned by the refresh query... even though its been deleted, but the JPA version does not.

Which is due to the cascade being configured slightly differently. The hbm version did not have the delete-orphan option.

I have now switched this on and I get the same behaviour from both - so I guess this is one for the core hibernate forum.

Note both are being tested with HSQL.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 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.