avoid calling evict?

6 Messages Forum Options Options
Embed this topic
Permalink
andiqo
avoid calling evict?
Reply Threaded More
Print post
Permalink
Hi,

A question for JPA experts in order to avoid calling evict, please.

I have a simple model made of 2 classes : Node and Link to modelize a graph in database (Link = many-to-many relationship). The Link class has a 'kind' attribute, in order to know if the association between Nodes is an aggregation or a composition. Thus, as I cannot use 'dependent-element' tag, I manage deletion by code.

So, when the user chooses to delete one node somewhere in the graph, a delete action manages it and implements a 'cascade-delete' in database for hard links. But I need to call evict potentially a lot of times, in order to avoid stale nodes...

Doing something like:

// --
  // Avoid stale children in other Node parents
  queryString = "SELECT FROM_ID FROM LINK AS l WHERE l.TO_ID=" + id;      
  query = getEntityManager().createNativeQuery(queryString);
  List<?> list = query.getResultList();
  for (Object parent : list) {
    queryString = "SELECT n FROM Node n WHERE n.id = " + (Long) parent;
    query = getEntityManager().createQuery(queryString);
    Node node = (Node) query.getSingleResult();
    OpenJPAEntityManager openJPAEM = OpenJPAPersistence.cast(getEntityManager());
    openJPAEM.evict(node);
  }
// --

Thus I create a native query using only ids, then a JPA query to get the Node object, then call evict.
But most of the time, the parent Node I tried to evict isn't even loaded in the EntityManager. Thus my question is, do you see better way to do this?

Thanks a lot for your help.

Nicolas
Pinaki Poddar
Re: avoid calling evict?
Reply Threaded More
Print post
Permalink
Hi,
  When an application using O-R mapping has to resort to SQL, it often signals some sort of limitation in domain model design and more importantly using SQL takes away one of the most important benefits of O-R mapping itself -- namely insulating your application from persistent database schema.

  The O-R mapping programming model prescribes that the application should operate on the entity domain only, the O-R mapping vendor should translate these operations to the persistent database schema both by structure as well as by operation.  

  Coming to this specific problem, you have two operations at your disposal for deletion. One in Java: Collection.remove(Object x) and other in JPA: EntityManager.remove(Object x).
As a Link knows whether it is an association or composition, hence when Node A delinks from Node B, Link(A,B) can determine whether to invoke Collection.remove() (i.e. A.myNeighbours.remove(B)) for association or EntityManager.remove() (i.e. em.remove(B)) for composition.

  The persistence provider (OpenJPA or otherwise) should take over from there and generate appropriate changes to database. If OpenJPA is not doing that then please furnish further details.

  What @ElementDependent does for OpenJPA is Collection.remove() becomes EntityManager.remove() -- so Java association appears to be composition.
andiqo
Re: avoid calling evict?
Reply Threaded More
Print post
Permalink
OK, thanks a lot for your answer.

Regards,

Nicolas
Craig L Russell
Re: avoid calling evict?
Reply Threaded More
Print post
Permalink
In reply to this post by andiqo
Hi Nicolas,

Pinaki has responded and my comments are intended to complement, not  
contradict his reply.

On Aug 26, 2008, at 3:30 AM, andiqo wrote:

>
> Hi,
>
> A question for JPA experts in order to avoid calling evict, please.
>
> I have a simple model made of 2 classes : Node and Link to modelize  
> a graph
> in database (Link = many-to-many relationship). The Link class has a  
> 'kind'
> attribute, in order to know if the association between Nodes is an
> aggregation or a composition. Thus, as I cannot use 'dependent-
> element' tag,
> I manage deletion by code.
Let's start with the modeling.I'm assuming that the Entity Link is  
mapped to a join table with two columns "to" and "from" representing  
the Nodes, and the columns contain foreign keys to Nodes. An  
additional column tells you whether the relationship is composition,  
for which cascade delete is appropriate.

So your Node has two Collections, one mapped to the "to" foreign key,  
and another mapped to the "from" foreign key. Your Node business logic  
needs to treat the union of these two collections as the collection of  
Links.
>
> So, when the user chooses to delete one node somewhere in the graph, a
> delete action manages it and implements a 'cascade-delete' in  
> database for
> hard links. But I need to call evict potentially a lot of times, in  
> order to
> avoid stale nodes...

The issue with stale nodes is an unfortunate decision by the JPA  
expert group to require that relationships must be explicitly managed  
by the user, regardless of whether the other side has even been  
brought into the EntityManager's context.

But beyond the JPA requirements, OpenJPA automatically does the  
appropriate unlinking for you if you delete instances in memory where  
the other side is not in cache and the other side doesn't otherwise  
need to be updated. So the only thing you need to worry about is  
deleting the Node on other side of the Link in case the Link is a  
composition.

When you delete a Node, you need to explicitly fetch the two  
collections of Links (to and from this Node) and check each link to  
decide whether to delete the Node on the other side or not.

You might implement a policy where the only Links that are allowed to  
cascade delete are Links in the "to" Collection. Then the logic is a  
little easier since you don't need to even look at cascade delete for  
links in the "from" collection. (I'm assuming that you never have the  
case where both "to" and "from" links can have cascade delete).

For Links that are not cascaded, all you need is to delete them using  
normal JPA delete. OpenJPA will unlink the Nodes on the other sides  
automatically.

For Links that are cascaded, you need to delete the target Node. You  
could simply invoke JPA delete on the Node on the "to" side. If you  
want to be more efficient, you can implement a query and do a group  
delete of the other Nodes.

List<Node> to_be_deleted = em.createQuery("Select n from Link l join  
l.to as n where l.from = :theNode and l.type =  
cascade").setParameters(node).listResult();
EntityManager.cast(em).removeAll(to_be_deleted);

Craig

>
>
> Doing something like:
>
> // --
>  // Avoid stale children in other Node parents
>  queryString = "SELECT FROM_ID FROM LINK AS l WHERE l.TO_ID=" + id;
>  query = getEntityManager().createNativeQuery(queryString);
>  List<?> list = query.getResultList();
>  for (Object parent : list) {
>    queryString = "SELECT n FROM Node n WHERE n.id = " + (Long) parent;
>    query = getEntityManager().createQuery(queryString);
>    Node node = (Node) query.getSingleResult();
>    OpenJPAEntityManager openJPAEM =
> OpenJPAPersistence.cast(getEntityManager());
>    openJPAEM.evict(node);
>  }
> // --
>
> Thus I create a native query using only ids, then a JPA query to get  
> the
> Node object, then call evict.
> But most of the time, the parent Node I tried to evict isn't even  
> loaded in
> the EntityManager. Thus my question is, do you see better way to do  
> this?
>
> Thanks a lot for your help.
>
> Nicolas
> --
> View this message in context: http://n2.nabble.com/avoid-calling-evict--tp783696p783696.html
> Sent from the OpenJPA Users mailing list archive at Nabble.com.
>
Craig L Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@...
P.S. A good JDO? O, Gasp!



smime.p7s (3K) Download Attachment
Pinaki Poddar
Re: avoid calling evict?
Reply Threaded More
Print post
Permalink
In reply to this post by andiqo
Hi,
  Sometimes ago, I blogged about "Persistence of a generic graph" in dev2dev.bea.com [1]. But after the merger that site is no more and so that blog content is only available in cached version of Google. So please pardon its scraggy look. Some of it may be relevant to this context

http://209.85.215.104/search?q=cache:kMQ5XfNjIWUJ:dev2dev.bea.com/blog/pinaki.poddar/archive/2007/08/+Persistence+Generic+Graph+Pinaki+Poddar&hl=en&ct=clnk&cd=1&gl=us&client=firefox-a

andiqo
Re: avoid calling evict?
Reply Threaded More
Print post
Permalink
Hi,

Thanks, I really appreciate you took time to answer such a "general" question!

I try to persist a graph in database and I found your link, Pinaki, really interesting. My concern is a little bit different as my "main entity" is the Node (a Node is potentially connected to other Node(s), but not necessarily).

Here are 2 classes of my domain object (renamed fields: to = target; from = source):

@Entity
public class Node {
   ...
   @ManyToMany(mappedBy = "target")
   @ElementDependent
   private Set<Edge> sources = new HashSet<Edge>();

   @ManyToMany(mappedBy = "source")
   @ElementDependent
   private Set<Edge> targets = new HashSet<Edge>();
   ...
}

@Entity
public class Edge {
   ...
   @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
   // Manage deletion by code
   private Node source;

   @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
   // Manage deletion by code
   private Node target;

   /** The qualifier of this relationship */
   @Basic(fetch = FetchType.LAZY)
   private int kind;
   ...
}

You're right Graig, I only wish to cascade delete the target Nodes (and not the 'sources' collection). I thought using the 'kind' attribute in order to distinguish between cascade delete or not (of course, only one cascaded-link could reach one Node) but i should rather add a 'cascaded' attribute if I want the 'kind' to stay the qualifier of the link.

Perhaps a better option is to only cascade-delete a Node, if there is no more link pointing to it (taking care not to loop several times over Nodes). Thus the 'cascaded' attribute would be useless. I have to think about it...

Thanks a lot!

Nicolas