You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2010/06/17 01:22:31 UTC

svn commit: r955425 - /openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/

Author: ppoddar
Date: Wed Jun 16 23:22:31 2010
New Revision: 955425

URL: http://svn.apache.org/viewvc?rev=955425&view=rev
Log:
OPENJPA-1686: Define generic persistent graph and relation. Implement graph as a set of edges.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentRelation.java
      - copied, changed from r954593, openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java   (with props)
Removed:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Vertex.java
Modified:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/City.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/People.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/TestPersistentGraph.java

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java?rev=955425&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java Wed Jun 16 23:22:31 2010
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.persistence.graph;
+
+import java.util.AbstractSet;
+import java.util.Set;
+
+/**
+ * Abstract implementation of a {@linkplain Graph} borrows from {@link AbstractSet abstract} implementation of 
+ * {@link Set}. The extended {@link Set#remove(Object) remove()} semantics accounts for 
+ * {@link Graph#delink(Object, Object) removal} of all relationship to the removed element.
+ * 
+ * @author Pinaki Poddar
+ *
+ * @param <E> type of element of the graph.
+ */
+public abstract class AbstractGraph<E> extends AbstractSet<E> implements Graph<E> {
+    /**
+     * Removing an element from this graph has the side effect of removing all 
+     * relations directed to the removed element.
+     */
+    @Override
+    public boolean remove(Object e) {
+        E node = (E)e;
+        Set<Relation<E, E>> rs = getRelationsTo(node);
+        for (Relation<E,E> r : rs) {
+            delink(r.getSource(), node);
+        }
+        return super.remove(e);
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/AbstractGraph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/City.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/City.java?rev=955425&r1=955424&r2=955425&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/City.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/City.java Wed Jun 16 23:22:31 2010
@@ -18,20 +18,20 @@
  */
 package org.apache.openjpa.persistence.graph;
 
+import java.io.Serializable;
+
 import javax.persistence.Entity;
 import javax.persistence.Id;
 
 /**
  * A simple persistent entity to become member of a graph.
- * In this style, a type has to extend {@linkplain Vertex} - an abstract persistent type.
- * This persistent type has its own identity.
  * 
  * @author Pinaki Poddar
  *
  */
 @SuppressWarnings("serial")
 @Entity
-public class City extends Vertex<City> {
+public class City implements Serializable {
     @Id
     private String name;
 

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java?rev=955425&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java Wed Jun 16 23:22:31 2010
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.persistence.graph;
+
+import java.util.Set;
+
+/**
+ * Graph is an extended {@link java.util.Set Set} that is aware of {@link Relation relationship} 
+ * between its elements. The linkage between two elements is represented by a {@link Relation relation}.
+ * <br>
+ * The extended behavior of Set allows two member elements to be {@linkplain Graph#link(Object, Object) linked} 
+ * or {@linkplain Graph#delink(Object, Object) delinked}. 
+ * 
+ * @param <E> Type of element of the graph
+ * 
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public interface Graph<E> extends Set<E> {
+    /**
+     * Links the pair of given vertices.
+     * If the pair is already linked then the existing relation is returned.
+     * If either of the vertices are currently non a member of this graph,
+     * then they are added to the graph as a side-effect of linking.
+     *  
+     * @param source non-null source node 
+     * @param target non-null target node
+     * 
+     * @param <V1> generic type of source node 
+     * @param <V2> generic type of target node
+     *
+     * @return a relation 
+     */
+    public <V1 extends E, V2 extends E> Relation<V1, V2> link(V1 source, V2 target);
+
+    /**
+     * Breaks the relation between the given pair of nodes.
+     * 
+     * @param source non-null source node 
+     * @param target non-null target node
+     * 
+     * @param <V1> generic type of source node 
+     * @param <V2> generic type of target node
+     * 
+     * @return the existing relation, if any, that had been broken. null otherwise.
+     */
+    public <V1 extends E, V2 extends E> Relation<V1, V2> delink(V1 source, V2 target);
+
+    /**
+     * Gets the directed relation between the given pair of nodes, if exists.
+     *  
+     * @param source non-null source node 
+     * @param target non-null target node
+     * 
+     * @param <V1> generic type of source node 
+     * @param <V2> generic type of target node
+     *
+     * @return a relation between the nodes, if exists. null otherwise. 
+     */
+    public <V1 extends E, V2 extends E> Relation<V1, V2> getRelation(V1 source, V2 target);
+    
+    /**
+     * Gets the nodes that are directly reachable from the given source node.
+     * 
+     * @return set of target nodes. Empty set if the given source node is not connected to any other nodes.
+     */
+    public Set<E> getTargets(E source);
+    
+    
+    /**
+     * Gets the source nodes that are directly connected to the given target node.
+     * 
+     * @return set of source nodes. Empty set if the given target node is not connected from any node.
+     */
+    public Set<E> getSources(E target);
+    
+    /**
+     * Gets all the relations originating from the given source.
+     * @param <V>
+     * @param source
+     * @return
+     */
+    public <V extends E> Set<Relation<V,E>> getRelationsFrom(V source);
+    
+    /**
+     * Gets all the relations terminating on the given target.
+     * @param <V>
+     * @param target
+     * @return
+     */
+    public <V extends E> Set<Relation<E,V>> getRelationsTo(V target);
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Graph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/People.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/People.java?rev=955425&r1=955424&r2=955425&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/People.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/People.java Wed Jun 16 23:22:31 2010
@@ -18,23 +18,24 @@
  */
 package org.apache.openjpa.persistence.graph;
 
+import java.io.Serializable;
+
 import javax.persistence.Entity;
 import javax.persistence.Id;
 
 /**
  * A simple persistent entity to become member of a graph.
- * In this style, a type has to extend {@linkplain Vertex} - an abstract persistent type.
- * This persistent type has its own identity.
  * 
  * @author Pinaki Poddar
  *
  */
 @SuppressWarnings("serial")
 @Entity
-public class People extends Vertex<People>{
+public class People implements Serializable {
     @Id
     private long ssn;
     private String name;
+    
     public long getSsn() {
         return ssn;
     }

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java?rev=955425&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java Wed Jun 16 23:22:31 2010
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.persistence.graph;
+
+import java.io.Serializable;
+
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Persistent Graph is a marker for persistent version of a {@link Graph}.
+ * <br>
+ * A persistent graph does not declare how the persistent state of the graph is captured and hence is abstract.
+ * But Persistent Graph is unique to be persistent in a JPA sense, because JPA does <em>not</em> represent 
+ * container instances such as {@link java.util.Set} or {@link java.util.List} as <em>first class</em> type --
+ * i.e. their instances do not carry a persistent identity. But declaring a graph as persistent type amounts
+ * to represent the graph as a first class persistent type with its own persistent identity.  
+ * <br>
+ * Persistent Graph defines a auto-generated persistent identity.
+ * 
+ * @author Pinaki Poddar
+ *
+ * @param <E> type of element.
+ */
+@SuppressWarnings("serial")
+@MappedSuperclass
+public abstract class PersistentGraph<E> extends AbstractGraph<E> implements Serializable {
+    
+    @Id
+    @GeneratedValue
+    private long id;
+    
+    public final long getId() {
+        return id;
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentGraph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentRelation.java (from r954593, openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java)
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentRelation.java?p2=openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentRelation.java&p1=openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java&r1=954593&r2=955425&rev=955425&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/PersistentRelation.java Wed Jun 16 23:22:31 2010
@@ -30,17 +30,20 @@ import javax.persistence.ManyToMany;
 import javax.persistence.OneToOne;
 
 /**
- * Generic, directed, attributed Relation as a first-class entity.
+ * Generic, directed, attributed Relation as a first-class, persistent entity.
  * <br>
  * A relation is 
  * <ol>
- * <LI>generic because the vertices it links are generically typed.
- * <LI>directed because it distinguishes the two end points as source and target.
+ * <LI>generic because Relation type is parameterized with the type of vertices it links.
+ * <LI>directed because it distinguishes the two vertices as source and target.
  * <LI>attributed because any arbitrary name-value pair can be associated with a relation.
  * </ol>
+ * A relation is made persistence capable by annotating its generic source and target as persistent entity.
+ * A relation is also a <em>first-class</em> entity having its own persistent identifier.
+ * <br>
  * A relation is immutable in terms of its two vertices. The properties
- * attached to a relation can change.
- *
+ * attached to a relation, however, can change.
+ * <br>
  * @param <V1> the type of <em>source</em> vertex linked by this relation.
  * @param <V2> the type of <em>target</em> vertex linked by this relation.
  *  
@@ -49,7 +52,7 @@ import javax.persistence.OneToOne;
  */
 @SuppressWarnings("serial")
 @Entity
-public class Relation<V1,V2> implements Serializable {
+public class PersistentRelation<V1,V2> implements Relation<V1,V2>, Serializable {
     /**
      * Relation is a first class object with its own identifier.
      */
@@ -60,14 +63,16 @@ public class Relation<V1,V2> implements 
     /**
      * A Relation must have a non-null vertex as source.
      */
-    @OneToOne(optional=false)
-    private Vertex<V1> source;
+    @OneToOne(optional=false, targetEntity=Entity.class, 
+            cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
+    private V1 source;
     
     /**
-     * A Relation must have a non-null vertex as source.
+     * A Relation may have a non-null vertex as target.
      */
-    @OneToOne(optional=false)
-    private Vertex<V2> target;
+    @OneToOne(optional=true, targetEntity=Entity.class,
+            cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
+    private V2 target;
     
     /**
      * The properties of a Relation is a set of key-value pairs and is declared as 
@@ -102,18 +107,18 @@ public class Relation<V1,V2> implements 
     /**
      * Special constructor for byte code enhancement.
      */
-    protected Relation() {
+    protected PersistentRelation() {
     }
     
     /**
      * A relation is immutable in terms of two vertices it connects.
-     * Either vertex must not be null.
+     * 
+     * @param s source vertex must not be null.
+     * @param t target vertex may or may not be null.
      */
-    public Relation(Vertex<V1> s, Vertex<V2> t) {
+    public PersistentRelation(V1 s, V2 t) {
         if (s == null)
             throw new NullPointerException("Can not create relation from a null source vertex");
-        if (t == null)
-            throw new NullPointerException("Can not create relation to a null target vertex");
         source = s;
         target = t;
         attrs = new Properties();
@@ -128,15 +133,15 @@ public class Relation<V1,V2> implements 
     
     /**
      * Gets the immutable source vertex.
-    */
-    public Vertex<V1> getSource() {
+     */
+    public V1 getSource() {
         return source;
     }
     
     /**
      * Gets the immutable target vertex.
-    */
-    public Vertex<V2> getTarget() {
+     */
+    public V2 getTarget() {
         return target;
     }
     
@@ -181,4 +186,8 @@ public class Relation<V1,V2> implements 
         attrs.remove(attr);
         return this;
     }
+    
+    public String toString() {
+        return source + "->" + target;
+    }
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java?rev=955425&r1=955424&r2=955425&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/Relation.java Wed Jun 16 23:22:31 2010
@@ -18,167 +18,76 @@
  */
 package org.apache.openjpa.persistence.graph;
 
-import java.io.Serializable;
 import java.util.Properties;
 
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Id;
-import javax.persistence.ManyToMany;
-import javax.persistence.OneToOne;
-
 /**
- * Generic, directed, attributed Relation as a first-class entity.
+ * Generic, directed, attributed Relation.
  * <br>
  * A relation is 
  * <ol>
- * <LI>generic because the vertices it links are generically typed.
- * <LI>directed because it distinguishes the two end points as source and target.
- * <LI>attributed because any arbitrary name-value pair can be associated with a relation.
+ * <LI>generic because Relation type is parameterized with the type of vertices it links.
+ * <LI>directed because it distinguishes the two vertices as source and target.
+ * <LI>attributed because any arbitrary key-value pair can be associated with a relation.
  * </ol>
+ * <br>
  * A relation is immutable in terms of its two vertices. The properties
- * attached to a relation can change.
- *
+ * associated to a relation, however, can change.
+ * <br>
  * @param <V1> the type of <em>source</em> vertex linked by this relation.
  * @param <V2> the type of <em>target</em> vertex linked by this relation.
  *  
  * @author Pinaki Poddar
  *
  */
-@SuppressWarnings("serial")
-@Entity
-public class Relation<V1,V2> implements Serializable {
+public interface Relation<V1,V2> {
     /**
-     * Relation is a first class object with its own identifier.
-     */
-    @Id
-    @GeneratedValue
-    private long id;
-    
-    /**
-     * A Relation must have a non-null vertex as source.
-     */
-    @OneToOne(optional=false)
-    private Vertex<V1> source;
-    
-    /**
-     * A Relation must have a non-null vertex as source.
+     * Gets the immutable source vertex.
+     * 
+     * @return a non-null source vertex.
      */
-    @OneToOne(optional=false)
-    private Vertex<V2> target;
+    public V1 getSource();
     
     /**
-     * The properties of a Relation is a set of key-value pairs and is declared as 
-     * <code>java.util.Properties</code>.
-     * <br>
-     * Declaring the key-value pairs as <code>java.util.Properties</code> makes OpenJPA
-     * assume that both key and value will be stored in database as String.
-     * This is not <em>strictly</em> correct because <code>java.util.Properties</code>
-     * declares its key and value as <code>java.lang.Object</code>. Hence it is possible for an application
-     * to insert key and/or value that are not a String but that type information will not be preserved in
-     * the database. Subsequently, when loaded from database the key and value
-     * both will appear as String and hence it becomes the application's responsibility to decode the
-     * Strings back to the actual type. While this provision loses type information, it allows the
-     * database record to be readable and more importantly supports query that are predicated on 
-     * (equality only) key-value pairs.
-     * <br>
-     * Another possibility to express key-value pair as
-     * <br>
-     * <code>Map<String,Serializable> attrs;</code>
-     * <br> 
-     * This will serialize the values but preserve their types. The down-side is neither a query can be 
-     * predicated on value nor are the database records readable.  
-     * <br>
-     * The third alternative is a Map where keys are String and values are Object 
-     * <br>
-     * <code>Map<String,Object> attrs;</code>
-     * This leads to the whole map being serialized as a single blob of data.
+     * Gets the immutable target vertex.
+     * Unlike source, a target for a relation may be null.
+     * 
+     * @return a target vertex. May be null.
      */
-    @ManyToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
-    private Properties attrs;
+    public V2 getTarget();
     
-    /**
-     * Special constructor for byte code enhancement.
-     */
-    protected Relation() {
-    }
     
     /**
-     * A relation is immutable in terms of two vertices it connects.
-     * Either vertex must not be null.
-     */
-    public Relation(Vertex<V1> s, Vertex<V2> t) {
-        if (s == null)
-            throw new NullPointerException("Can not create relation from a null source vertex");
-        if (t == null)
-            throw new NullPointerException("Can not create relation to a null target vertex");
-        source = s;
-        target = t;
-        attrs = new Properties();
-    }
-    
-    /**
-     * Gets generated persistent identity.
+     * Adds the given key-value pair, overwriting any prior value associated to the same key.
+     * 
+     * @return the same relation for <em>fluent</em> method-chaining
      */
-    public long getId() {
-        return id;
-    }
-    
-    /**
-     * Gets the immutable source vertex.
-    */
-    public Vertex<V1> getSource() {
-        return source;
-    }
-    
-    /**
-     * Gets the immutable target vertex.
-    */
-    public Vertex<V2> getTarget() {
-        return target;
-    }
+    public Relation<V1,V2> addAttribute(String key, Object value);
     
     /**
-     * Affirms if the given attribute is associated with this relation.
+     * Affirms if an attribute value has been associated with the given key.
+     * 
      */
-    public boolean hasAttribute(String attr) {
-        return attrs.containsKey(attr);
-    }
+    public boolean hasAttribute(String key);
     
     /**
      * Gets the value of the given attribute.
      * 
      * @return value of the given attribute. A null value does not distinguish whether
      * the attribute was set to a null value or the attribute was absent. 
-     */
-    public Object getAttribute(String attr) {
-        return attrs.get(attr);
-    }
-    
-    public Properties getAttributes() {
-        return attrs;
-    }
-
-    /**
-     * Adds the given key-value pair, overwriting any prior association to the same attribute.
      * 
-     * @return the same relation for fluent method-chaining
+     * @see #hasAttribute(String)
      */
-    public Relation<V1,V2> addAttribute(String attr, Object v) {
-        attrs.put(attr, v);
-        return this;
-    }
+    public Object getAttribute(String key);
     
     /**
      * Removes the given attribute.
      * 
-     * @return value of the given attribute that just has been removed. A null value does not 
-     * distinguish whether the attribute was set to a null value or the attribute was absent. 
+     * @return the modified relation for <em>fluent</em> method chaining.
+     */
+    public Relation<V1,V2> removeAttribute(String key);
+    
+    /**
+     * Gets the key-value pairs associated with this relation.
      */
-    public Relation<V1,V2> removeAttribute(String attr) {
-        attrs.remove(attr);
-        return this;
-    }
+    public Properties getAttributes();
 }

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java?rev=955425&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java Wed Jun 16 23:22:31 2010
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package org.apache.openjpa.persistence.graph;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+
+/**
+ * RelationGraph is a first-class persistent entity that express its persistent state as a set of
+ * {@link Relation persistent relations}.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+
+@SuppressWarnings("serial")
+@Entity
+public class RelationGraph<E> extends PersistentGraph<E>  {
+    @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
+    private Set<PersistentRelation<E,E>> relations = new HashSet<PersistentRelation<E,E>>();
+    
+    /* 
+     * Links the given vertices, unless they are already connected.
+     * 
+     * @param source non-null source vertex
+     * @param target non-null target vertex
+     * 
+     * @see org.apache.openjpa.persistence.graph.Graph#link(V1, V2)
+     */
+    public <V1 extends E,V2 extends E> Relation<V1,V2> link(V1 source, V2 target) {
+        if (source == null)
+            throw new NullPointerException("Can not link from a null source vertex");
+        if (target == null)
+            throw new NullPointerException("Can not link to a null target vertex");
+        
+        Relation<V1,V2> r = getRelation(source, target);
+        if (r == null) {
+            r = new PersistentRelation<V1, V2>(source, target);
+            relations.add((PersistentRelation<E, E>) r);
+        }
+        return r;
+        
+    }
+    
+    /* 
+     * Delinks the given vertices, if they are currently connected.
+     * 
+     * 
+     * @see org.apache.openjpa.persistence.graph.Graph#delink(V1, V2)
+     */
+    public <V1 extends E,V2 extends E> Relation<V1,V2> delink(V1 source, V2 target) {
+        Relation<V1,V2> r = getRelation(source, target);
+        if (r != null) {
+            relations.remove(r);
+        }
+        return r;
+        
+    }
+    
+    /* 
+     * Get the relation between the given vertex.
+     *  
+     * @see org.apache.openjpa.persistence.graph2.Graph#getRelation(V1, V2)
+     */
+    public <V1 extends E,V2 extends E> Relation<V1,V2> getRelation(V1 source, V2 target) {
+        for (Relation<?,?> r : relations) {
+            if (r.getSource().equals(source) && r.getTarget() != null && r.getTarget().equals(target)) {
+                return (Relation<V1,V2>)r;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Iterator over the nodes of this graph.
+     */
+    public Iterator<E> iterator() {
+        return getNodes().iterator();
+    }
+    
+    public int size() {
+        return 0;
+    }
+
+    @Override
+    public <V extends E> Set<Relation<V, E>> getRelationsFrom(V source) {
+        Set<Relation<V,E>> rs = new HashSet<Relation<V,E>>();
+        for (Relation<E,E> r : relations) {
+            if (r.getSource().equals(source) && r.getTarget() != null)
+                rs.add((Relation<V,E>)r);
+        }
+        return rs;
+    }
+
+    @Override
+    public <V extends E> Set<Relation<E, V>> getRelationsTo(V target) {
+        Set<Relation<E, V>> rs = new HashSet<Relation<E, V>>();
+        for (Relation<?,?> r : relations) {
+            if (r.getTarget() != null && r.getTarget().equals(target))
+                rs.add((Relation<E, V>)r);
+        }
+        return rs;
+    }
+
+    @Override
+    public Set<E> getSources(Object target) {
+        Set<E> sources = new HashSet<E>();
+        for (Relation<E,E> r : relations) {
+            if (r.getTarget() != null && r.getTarget().equals(target))
+                sources.add(r.getSource());
+        }
+        return sources;
+    }
+
+    @Override
+    public Set<E> getTargets(Object source) {
+        Set<E> targets = new HashSet<E>();
+        for (Relation<E,E> r : relations) {
+            if (r.getSource().equals(source) && r.getTarget() != null)
+                targets.add(r.getTarget());
+        }
+        return targets;
+    }
+    
+    public Set<E> getNodes() {
+        Set all = new HashSet();
+        for (Relation<?,?> r : relations) {
+            all.add(r.getSource());
+            if (r.getTarget() != null)
+                all.add(r.getTarget());
+        }
+        return all;
+    }
+    
+    @Override
+    public boolean add(E e) {
+        if (contains(e))
+            return false;
+        relations.add(new PersistentRelation<E,E>(e,null));
+        return true;
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/RelationGraph.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/TestPersistentGraph.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/TestPersistentGraph.java?rev=955425&r1=955424&r2=955425&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/TestPersistentGraph.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/graph/TestPersistentGraph.java Wed Jun 16 23:22:31 2010
@@ -32,13 +32,11 @@ import org.apache.openjpa.jdbc.meta.Mapp
 import org.apache.openjpa.jdbc.meta.ValueHandler;
 import org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.HandlerHandlerMapTableFieldStrategy;
-import org.apache.openjpa.jdbc.meta.strats.RelationCollectionInverseKeyFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.UntypedPCValueHandler;
 import org.apache.openjpa.kernel.QueryHints;
 import org.apache.openjpa.meta.ClassMetaData;
 import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.meta.MetaDataRepository;
-import org.apache.openjpa.persistence.OpenJPAPersistence;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 /**
@@ -80,23 +78,23 @@ public class TestPersistentGraph extends
     };
 
     private EntityManager em;
-
+    private PersistentGraph<Object> graph;
+    
     public void setUp() throws Exception {
-        super.setUp(CLEAR_TABLES, Vertex.class, Relation.class, People.class, City.class);
+        super.setUp(CLEAR_TABLES, PersistentGraph.class, RelationGraph.class,
+                PersistentRelation.class, People.class, City.class);
         em = emf.createEntityManager();
-        createData();
+        graph = createData();
         em.clear();
     }
-
+    
     /**
      * Verifies that fields are mapped with expected strategy or value handlers.
      */
     public void testMapping() {
-        assertStrategy(People.class, "relations", RelationCollectionInverseKeyFieldStrategy.class, null);
-        assertStrategy(City.class, "relations", RelationCollectionInverseKeyFieldStrategy.class, null);
-        assertStrategy(Relation.class, "source", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
-        assertStrategy(Relation.class, "target", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
-        assertStrategy(Relation.class, "attrs", HandlerHandlerMapTableFieldStrategy.class, null);
+        assertStrategy(PersistentRelation.class, "source", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
+        assertStrategy(PersistentRelation.class, "target", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
+        assertStrategy(PersistentRelation.class, "attrs", HandlerHandlerMapTableFieldStrategy.class, null);
     }
 
     private void printMapping(FieldMapping fm) {
@@ -111,7 +109,7 @@ public class TestPersistentGraph extends
     }
 
     FieldMapping getFieldMapping(Class<?> pcClass, String field) {
-        MappingRepository repos = (MappingRepository) OpenJPAPersistence.cast(emf).getConfiguration()
+        MappingRepository repos = (MappingRepository) emf.getConfiguration()
                 .getMetaDataRepositoryInstance();
         ClassMapping cmd = repos.getMapping(pcClass, null, true);
         assertNotNull("No metadata found for " + pcClass, cmd);
@@ -123,7 +121,7 @@ public class TestPersistentGraph extends
 
     /**
      * Asserts that the given field of the given class has been mapped with the
-     * given straegy or value handler.
+     * given strategy or value handler.
      */
     void assertStrategy(Class<?> pcClass, String field, Class<? extends FieldStrategy> strategy,
             Class<? extends ValueHandler> handler) {
@@ -151,7 +149,7 @@ public class TestPersistentGraph extends
     }
 
     FieldStrategy getStrategy(Class<?> cls, String field) {
-        MetaDataRepository repos = OpenJPAPersistence.cast(emf).getConfiguration().getMetaDataRepositoryInstance();
+        MetaDataRepository repos = emf.getConfiguration().getMetaDataRepositoryInstance();
         ClassMetaData cmd = repos.getMetaData(cls, null, true);
         assertNotNull("No metadat found for " + cls, cmd);
         FieldMetaData fmd = cmd.getField(field);
@@ -168,6 +166,9 @@ public class TestPersistentGraph extends
      */
     public void testCreateGraph() {
         em.getTransaction().begin();
+        assertFalse(em.contains(graph));
+        graph = em.find(PersistentGraph.class, graph.getId());
+        assertNotNull(graph);
         People[] people = new People[SSN.length];
         for (int i = 0; i < SSN.length; i++) {
             People p = em.find(People.class, SSN[i]);
@@ -180,7 +181,7 @@ public class TestPersistentGraph extends
             assertNotNull(c);
             cities[i] = c;
         }
-        assertDataEquals(people, cities);
+        assertDataEquals(graph, people, cities);
 
         em.getTransaction().rollback();
     }
@@ -190,10 +191,11 @@ public class TestPersistentGraph extends
      * correctly.
      */
     public void testQueryRelation() {
-        List<Relation> relations = em.createQuery("select r from Relation r", Relation.class).getResultList();
+        String jpql = "select r from PersistentRelation r";
+        List<PersistentRelation> relations = em.createQuery(jpql, PersistentRelation.class).getResultList();
         for (Relation<?, ?> r : relations) {
-            Vertex<?> source = r.getSource();
-            Vertex<?> target = r.getTarget();
+            Object source = r.getSource();
+            Object target = r.getTarget();
             if (source instanceof People) {
                 int i = indexOf((People) source);
                 if (target instanceof People) {
@@ -204,16 +206,16 @@ public class TestPersistentGraph extends
                     int j = indexOf((City) target);
                     assertEquals(i % CITY_NAMES.length, j);
                     assertTrue(r.getAttributes().isEmpty());
-                } else {
-                    fail();
+                } else if (target != null){
+                    fail("Unexpected relation " + r);
                 }
             } else if (source instanceof City) {
                 int i = indexOf((City) source);
                 if (target instanceof City) {
                     int j = indexOf((City) target);
                     assertEquals(""+ATTR_DISTANCE_VALUE[i][j], r.getAttribute(ATTR_DISTANCE));
-                } else {
-                    fail();
+                } else if (target != null) {
+                    fail("Unexpected relation " + r);
                 }
             }
         }
@@ -224,8 +226,8 @@ public class TestPersistentGraph extends
      */
     public void testQueryRelationOnSourceParameter() {
         People p1 = em.find(People.class, SSN[0]);
-        String jpql = "select r from Relation r where r.source = :node";
-        List<Relation> result = em.createQuery(jpql, Relation.class)
+        String jpql = "select r from PersistentRelation r where r.source = :node";
+        List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
                                   .setParameter("node", p1)
                                   .getResultList();
         assertFalse("Result of [" + jpql + "] on source = " + p1 + " should not be empty", result.isEmpty());
@@ -235,8 +237,8 @@ public class TestPersistentGraph extends
      * Tests that a relation can be queried predicated on its attribute key.
      */
     public void testQueryRelationOnSingleAttributeKey() {
-        String jpql = "select r from Relation r join r.attrs a where key(a) = :key";
-        List<Relation> result = em.createQuery(jpql, Relation.class)
+        String jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
+        List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
                                   .setParameter("key", ATTR_EMOTION)
                                   .getResultList();
 
@@ -248,9 +250,9 @@ public class TestPersistentGraph extends
      * key-value pair.
      */
     public void testQueryRelationOnSingleAttributeKeyValue() {
-        String jpql = "select r from Relation r join r.attrs a where key(a) = :key and value(a) = :value";
+        String jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key and value(a) = :value";
         String value = EMOTIONS[0][2].toString();
-        List<Relation> result = em.createQuery(jpql, Relation.class)
+        List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
                                   .setParameter("key", ATTR_EMOTION)
                                   .setParameter("value", value)
                                   .getResultList();
@@ -265,10 +267,11 @@ public class TestPersistentGraph extends
      * wrong result.
      */
     public void testQueryRelationOnMultipleAttributeKeyValuePairs() {
-        String jpql = "select r from Relation r join r.attrs a1 join r.attrs a2 "
-                + "where key(a1) = :key1 and value(a1) = :value1 " + "and key(a2) = :key2 and value(a2) = :value2";
+        String jpql = "select r from PersistentRelation r join r.attrs a1 join r.attrs a2 "
+                    + "where key(a1) = :key1 and value(a1) = :value1 " 
+                    + "and key(a2) = :key2 and value(a2) = :value2";
         String value = EMOTIONS[0][2].toString();
-        List<Relation> result = em.createQuery(jpql, Relation.class)
+        List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
                                   .setParameter("key1", ATTR_EMOTION)
                                   .setParameter("value1", value)
                                   .setParameter("key2", ATTR_SINCE)
@@ -279,10 +282,10 @@ public class TestPersistentGraph extends
                 + ") and key-value=("  + ATTR_SINCE + "," + SINCE + ") should not be empty", 
                 result.isEmpty());
 
-        String wrongJPQL = "select r from Relation r join r.attrs a "  
+        String wrongJPQL = "select r from PersistentRelation r join r.attrs a "  
                          + "where key(a) = :key1 and value(a) = :value1 "
                          + "and key(a) = :key2 and value(a) = :value2";
-        List<Relation> result2 = em.createQuery(wrongJPQL, Relation.class)
+        List<PersistentRelation> result2 = em.createQuery(wrongJPQL, PersistentRelation.class)
                                    .setParameter("key1", ATTR_EMOTION)
                                    .setParameter("value1", value)
                                    .setParameter("key2", ATTR_SINCE)
@@ -297,8 +300,8 @@ public class TestPersistentGraph extends
     public void testAddRemoveAttribute() {
         em.getTransaction().begin();
         People p1 = em.find(People.class, SSN[0]);
-        String jpql = "select r from Relation r where r.source = :node";
-        List<Relation> r = em.createQuery(jpql, Relation.class)
+        String jpql = "select r from PersistentRelation r where r.source = :node";
+        List<PersistentRelation> r = em.createQuery(jpql, PersistentRelation.class)
                             .setHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true)
                             .setParameter("node", p1)
                             .getResultList();
@@ -308,8 +311,8 @@ public class TestPersistentGraph extends
         em.clear();
         
         em.getTransaction().begin();
-        jpql = "select r from Relation r join r.attrs a where key(a) = :key";
-        Relation newR = em.createQuery(jpql, Relation.class)
+        jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
+        Relation newR = em.createQuery(jpql, PersistentRelation.class)
                           .setParameter("key", "new-key")
                           .getSingleResult();
         assertNotNull(newR);
@@ -318,9 +321,9 @@ public class TestPersistentGraph extends
         em.getTransaction().commit();
         
         em.getTransaction().begin();
-        jpql = "select r from Relation r join r.attrs a where key(a) = :key";
+        jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
         try {
-            newR = em.createQuery(jpql, Relation.class)
+            newR = em.createQuery(jpql, PersistentRelation.class)
                           .setParameter("key", "new-key")
                           .getSingleResult();
             fail(jpql + " with new-key expected no result");
@@ -339,15 +342,15 @@ public class TestPersistentGraph extends
      * Creates a typical graph of People and Cities. The tests are sensitive to
      * the actual values and relations set in in this method.
      */
-    void createData() {
-        if (isPopulated())
-            return;
+    PersistentGraph<Object> createData() {
+        PersistentGraph<Object> graph = new RelationGraph<Object>();
+        
         em.getTransaction().begin();
 
         People[] people = new People[SSN.length];
         for (int i = 0; i < SSN.length; i++) {
             People p = new People();
-            em.persist(p);
+            graph.add(p);
             p.setSsn(SSN[i]);
             p.setName(PERSON_NAMES[i]);
             people[i] = p;
@@ -355,14 +358,15 @@ public class TestPersistentGraph extends
         City[] cities = new City[CITY_NAMES.length];
         for (int i = 0; i < CITY_NAMES.length; i++) {
             City c = new City();
-            em.persist(c);
+            graph.add(c);
             c.setName(CITY_NAMES[i]);
             cities[i] = c;
         }
         for (int i = 0; i < people.length; i++) {
             for (int j = 0; j < people.length; j++) {
                 if (EMOTIONS[i][j] != null) {
-                    Relation<People, People> r = people[i].link(people[j]).addAttribute(ATTR_EMOTION, EMOTIONS[i][j]);
+                    Relation<People, People> r = graph.link(people[i], people[j])
+                                 .addAttribute(ATTR_EMOTION, EMOTIONS[i][j]);
                     if (i == 0 && j == 2) {
                         r.addAttribute(ATTR_SINCE, SINCE);
                     }
@@ -371,18 +375,20 @@ public class TestPersistentGraph extends
         }
         for (int i = 0; i < cities.length; i++) {
             for (int j = 0; j < cities.length; j++) {
-                cities[i].link(cities[j]).addAttribute(ATTR_DISTANCE, ATTR_DISTANCE_VALUE[i][j]);
+                graph.link(cities[i], cities[j]).addAttribute(ATTR_DISTANCE, ATTR_DISTANCE_VALUE[i][j]);
             }
         }
 
         for (int i = 0; i < people.length; i++) {
-            people[i].link(cities[i % CITY_NAMES.length]);
+            graph.link(people[i], cities[i % CITY_NAMES.length]);
         }
-
+        em.persist(graph);
         em.getTransaction().commit();
+        
+        return graph;
     }
 
-    void assertDataEquals(People[] people, City[] cities) {
+    void assertDataEquals(Graph<Object> graph, People[] people, City[] cities) {
         assertEquals(SSN.length, people.length);
         assertEquals(CITY_NAMES.length, cities.length);
         
@@ -399,7 +405,7 @@ public class TestPersistentGraph extends
             People p1 = people[i];
             for (int j = 0; j < people.length; j++) {
                 People p2 = people[j];
-                Relation<People, People> r = p1.getRelationTo(p2);
+                Relation<People, People> r = graph.getRelation(p1,p2);
                 if (EMOTIONS[i][j] != null) {
                     assertNotNull(r);
                     assertEquals(EMOTIONS[i][j].toString(), r.getAttribute(ATTR_EMOTION));
@@ -412,7 +418,7 @@ public class TestPersistentGraph extends
             City c1 = cities[i];
             for (int j = 0; j < cities.length; j++) {
                 City c2 = cities[j];
-                Relation<City, City> r12 = c1.getRelationTo(c2);
+                Relation<City, City> r12 = graph.getRelation(c1,c2);
                 assertNotNull(r12);
                 assertEquals(""+ATTR_DISTANCE_VALUE[i][j], r12.getAttribute(ATTR_DISTANCE));
             }
@@ -422,7 +428,7 @@ public class TestPersistentGraph extends
             People p = people[i];
             for (int j = 0; j < cities.length; j++) {
                 City c = cities[j];
-                Relation<People, City> r = p.getRelationTo(c);
+                Relation<People, City> r = graph.getRelation(p,c);
                 if (i % CITY_NAMES.length == j) {
                     assertNotNull(r);
                 } else {